home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Belgian Amiga Club - ADF Collection
/
BS1 part 60.zip
/
BS1 part 60
/
LSD 32.adf
/
AmigaGfx4.pp
/
AmigaGfx4
Wrap
Text File
|
1990-09-07
|
198KB
|
5,089 lines
_________________________________________________________________________
/#########################################################################\
|#####...####..######..######......####...####..#####..##......##......###|
|####.....###..######..########..#####.....###...####..##..######..#######|
|###..###..##..######..########..####..###..##....###..##..######..#######|
|###..###..##..######..########..####..###..##..#...#..##..######......###|
|###.......##..######..########..####.......##..##.....##..######..#######|
|###..###..##..######..########..####..###..##..###....##..######..#######|
|###..###..##...#####...#######..####..###..##..####...##..######..#######|
|###..###..##......##......##......##..###..##..#####..##......##......###|
|#########################################################################|
|####################################################################{RB}#|
|=========================================================================|
| |
| ----> PRESENTS <---- |
| |
| AMIGA GRAPHICS INSIDE AND OUT - THE COMPLETE BOOK |
| |
| > PART 4 < |
| |
| Typed / Scanned / Edited By : RAZOR BLADE. |
| Additional Typing by : GLITCH ( + 8 pages by Asterix ! ). |
| Original Supplied by : VIPER |
| |
|-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-|
| CALL THE ALLIANCE WORLD HQ -> THE PLACE TO BE |
| UNKNOWN PLEASURES --> +44 (0) 823 322 891 --> SYSOP: BARBARIAN |
|_________________________________________________________________________|
CHAPTER 7 - THE IFF-ILBM STANDARD
=================================
"IFF" is the abbreviation for "Interchange File Format. This format evolved
from an agreement between two companies, Electronic Arts and Commodore when
the Amiga was just being introduced. These companies decided that it would
not be practical for users or programmers if each graphic program saved
graphics in a different format. If this happened then it is possible that
Graphicraft or Aegis pictures would only work with Aegis software. Without
a
a standard format exchanging pictures between programs would be impossible.
This is the reason why the standard format ILBM (InterLeave BitMap) was
created. An ILBM file consists of many components. The following are the
most important:
'BMHD' = BitMapHeaDer.
Identification: BMHDnnnn
Offset Type Description.
------- -------- -----------------------------------------------------
+000 Word Width of graphic.
+002 Word Height of graphic.
+004 Word X position this graphic.
+006 Word Y position this graphic.
+008 Byte Number of bitplanes (depth)
+009 Byte Masking
0 = No Masking.
1 = MAsking.
2 = Transparent.
3 = Lasso.
+010 Byte Data compression.
0 = No Compression.
1 = ByteRun1 algorithm.
+011 Byte Unused.
+012 Word "transparent" colour.
+014 Byte X aspect.
+015 Byte Y aspect.
+016 Word Width of Source Page.
+018 Word Height of Source Page.
PAGE 279
---------------------------------------------------------------------------
"CMAP" = ColorMap
Identification: CMAPnnnn
Offset Type Description
------- ------- ----------------------------------------------------------
+001 Byte Red (0-255)
+002 Byte Green (0-255)
+003 Byte Blue (0-255)
"BODY" = Bit-Planes.
Identification: BODYnnn
-----------------------------------------------------
Bit-plane 1
Bit-Plane 2
(...)
Bit-Plane n
------------------------------------------------------
"CAMG" = Amiga ViewPort Mode (Hires, lores, Lace, etc...)
Identificiation: CAMGnnnn
Offset Type Description
------- ------- ------------------------------------------------------
+000 Long ViewPort Mode (see chapter 4).
----------------------------------------------------------------------
Same as the color cycle information from GraphiCraft:
"CCRT" - raphiCraft ColorCycle Data.
Identification: "CCRTnnnn"
Offset Type Description.
------- ------- ----------------------------------------------
+000 Word Direction.
0 = Backwards.
1 = Forwards.
+002 Byte Start (number of colour register; 0-31)
+003 Byte End (number of colour register; 0-31)
+004 Long Seconds.
+008 Long Micro Seconds.
---------------------------------------------------------------
These important data blocks represent the ILBM format file for every
graphic saved using this method.
The folowing program demonstrates how to load and display this type of
file. It is possible for you to load any ILBM graphic that you want. For
example, you can load a graphic that was created with your drawing program
or an ILBM graphic from any other type of program.
PAGE 280
---------------------------------------------------------------------------
'####################################################
'#
'# Section: 7
'# Program: Load ILBM picture from disk.
'# Date : 01/15/87
'# Author : TOB
'# Version: 1.0
'#
'# Loads pictures of any mode, including HAM, halfbrite and
'# any GraphiCraft Color Cycle.
'#
'# Based on ILBM IFF Interleaved Bitmap Standard published November
'# 15, 1985 in the Rom kenal manual.
'#
'#######################################################
PRINT "Searching for .bmap files ........ "
'DOS-library
DECLARE FUNCTION xOpen& LIBRARY
DECLARE FUNCTION xRead& LIBRARY
DECLARE FUNCTION Seek& LIBRARY
'xClose()
'Delay()
'EXEC-library
DECLARE FUNCTION AllocMem& LIBRARY
DECLARE FUNCTION DoIO% LIBRARY
DECLARE FUNCTION OpenDevice% LIBRARY
DECLARE FUNCTION AllocSignal% LIBRARY
DECLARE FUNCTION FindTask& LIBRARY
'FreeMem()
'GRAPHICS-library
DECLARE FUNCTION AllocRaster& LIBRARY
'SetRast()
'LoadRGB4()
LIBRARY "dos.library"
LIBRARY "exec.library"
LIBRARY "graphics.library"
main: '* LOAD
PAGE 281
---------------------------------------------------------------------------
CLS
PRINT "ILBM Loader"
PRINT
PRINT "Loads Standard IFF Graphic Files into"
PRINT "memory and displays the Picture."
PRINT
PRINT "The IFF Loader supports GraphiCraft color cycling,"
PRINT " the display of Hold-and-Modify (4096 colors)"
PRINT " and Halfbrite (64 colours)."
PRINT
PRINT "In accordance with the ILBM standards it decodes packed"
PRINT "graphic files that use the ByteRun1 method and "
PRINT "displays them."
PRINT
PRINT "If desired, you can print the picture on a graphics"
PRINT " capable printer."
LINE INPUT "Name of IBM File : ";ilbm.file$
LINE INPUT "Do you wantthe picture printed (Y/N) ? ";yn$
LoadILBM ilbm.file$
ColorCycle -1
IF yn$ = "y" THEN
WINDOW OUTPUT 2
Hardcopy
END IF
WHILE INKEY$ = "":WEND
ILBMend
LIBRARY CLOSE
Sub LoadILBM(ilbm.name$) STATIC
SHARED disk.handle&, buf.read&
SHARED buf.color&, but.rgb&
SHARED ilbm.error$, signal%
SHARED screen.kolor%, amiga.viewport&
SHARED ilbm.vp.mode&
SHARED amiga.rastport&
disk.modeOldFile% = 1005
disk.name$ = ilbm.name+CHR$(0)
'* Open ILBM file
disk.handle& = xOpen&(SADD(disk.name%),1005)
IF disk.handle& = 0 THEN
PAGE 282
---------------------------------------------------------------------------
ilbm.error$ = "ILBM File "+ilbm.name$+" not on specified disk
and drawer. "
GOTO ilbm.error.A
END IF
'* Setup disk read buffer.
mem.opt& = 2^0+2^16
buf.size& = 240
buf.add& = AllocMem&(buf.size&,mem.opt&)
IF buf.add& = 0 THEN
ilbm.error$ = "Not enough buffer memory free."
GOTO ilbm.error.A
END IF
'* Section buffer for chunk areas.
buf.read& = buf.add&+0*120
buf.rgb& = buf.add&+1*120
'* Is it an ILBM file ???
disk.wasread& = xRead&(disk.handle&,buf.read&,12)
ilbm.ID.$ = ""
FOR loop1% = 8 TO 11
ilbm.ID.$ = ilbm.ID.$ + CHR$(PEEK,buf.read&+loop1%))
NEXT loop1%
IF ilbm.ID.$ <> "ILBM" THEN
ilbm.error$ = "File ":+ilbm.name$+" is not an ILBM file."
GOTO ilbm.error.A
END IF
'* read data chunks from ILBM
WHILE (signal%<>1) AND (ilbm.error$ = "")
ReadChunk
WEND
'* Error ?
ilbm.error.A:
IF ilbm.error$<>"" THEN
WINDOW CLOSE 2
SCREEN CLOSE 1
PRINT ilbm.error$
EXIT SUB
END IF
'* All ok
CALL LoadRGB4(amiga.viewport&, buf.rgb&, screen.kolor%)
'* Close ILBM file?
IF disk.handle & <> 0 THEN
CALL xClose(disk.handle&)
END IF
IF buf.add& <> 0 THEN
CALL FreeMem(buf.add&,buf.size&)
PAGE 283
---------------------------------------------------------------------------
buf.add& = 0
END IF
'* Set ViewModes.
POKEW amiga.viewport&+32, ilbm.vp.mode&
END SUB
SUB ILBMend STATIC
WINDOW CLOSE 2
SCREEN CLOSE 1
END SUB
SUB ColorCycle(mode%) STATIC
SHARED ccrt.direction%
SHARED ccrt.start%, amiga.colortable&
SHARED ccrt.end%, screen.kolor%
SHARED ccrt.secs&, status%
SHARED ccrt.mics&, amiga.viewport&
'* As intended.
IF (status% AND 2^4) <> 2^4 THEN
EXIT SUB
END IF
'* Setup varaible field
DIM kolor.original%(screen.kolor%-1)
DIM kolor.actual%(screen.kolor%-1)
'* all OK, save old color from ViewPort.
FOR loop1% = 0 TO screen.kolor%-1
kolor.original%(loop1%) = PEEKW(amiga.colortable&+2*loop1%)
kolor.actual%(loop1%) = kolor.original%(loop1%)
NEXT loop1%
'* Colour cycling.
WHILE mode%<>0
'* Mode ?
IF mode% < 0 THEN
in$ = INKEY$
IF in$<>"" THEN mode% = 0
ELSE
mode% = mode% - 1
END IF
'* Forwards ?
IF ccrt.direction% = 1 THEN
ccrt.backup% = kolor.actual%(ccrt.start%)
FOR loop1% = ccrt.start%+1 TO ccrt.end%
kolor.actual%(loop1%-1)=kolor.actual%(loop1%)
NEXT loop1%
kolor.actual%(ccrt.end%) = ccrt.backup%
'* Backwards.
ELSE
PAGE 284
---------------------------------------------------------------------------
ccrt.backup% = kolor.actual%(ccrt.end%)
FOR loop1% = ccrt.start%-1 TO ccrt.end% STEP -1
kolor.actual%(loop1%+1) = kolor.actual%(loop1%)
NEXT loop1%
kolor.actual%(ccrt.start%) = ccrt.backup%
END IF
CALL LoadRGB4(amiga.viewport&,VARPTR(kolor.actual%(0)),
screen.kolor%)
timeout& = 50*(ccrt.secs&+(ccrt.mics&/1000000&))
CALL Delay(timeout&)
WEND
'* Restore original colors.
CALL LoadRGB4(amiga.viewport&,VARPTR(kolor.original%(0)),screen.kolor%)
'* REturn fields.
ERASE kolor.original%
ERASE kolor.actual%
END SUB
SUB ReadChunk STATIC
SHARED disk.handle&, buf.read&
SHARED buf.color&, buf.rgb&
SHARED ilbm.error$, signal%
SHARED screen.kolor%, amiga.viewport&
SHARED ilbm.vp.mode&, status%
SHARED amiga.rastport&
SHARED ccrt.direction%
SHARED ccrt.start%, amiga.colortable&
SHARED ccrt.end%, screen.kolor%
SHARED ccrt.secs&
SHARED ccrt.mics&
'* Read chunk header.
disk.wasread& = xRead&(disk.handle&, buf.read&, 8)
ilbm.chunk& = PEEKL(buf.read&+4)
ilbm.ID.$ = ""
FOR loop1% = 0 TO 3
ilbm.ID.$ = ilbm.ID.$+CHR$(PEEK(buf.read&+loop1%))
NEXT loop1%
'* The bitmap Header (BMHD) ?
IF ilbm.ID.$ = "BMHD" THEN
'* Read chunk contents.
disk.wasread& = xRead&(disk.handle&,buf.read&,ilbm.chunk&)
status% = status% OR 2^0
ilbm.width% = PEEKW(buf.read&+0)
ilbm.height% = PEEKW(buf.read&+2)
ilbm.depth% = PEEK(buf.read&+8)
ilbm.mode% = PEEK(buf.read&+10)
PAGE 285
---------------------------------------------------------------------------
screen.width& = PEEKW(buf.read&+16)
screen.height% = PEEKW(buf.read&+18)
'* Build Display Parameters.
ilbm.bytes% = ilbm.width%/8
screen.bytes% = screen.width%/8
screen.kolor% = 2^(ilbm.depth%)
'* prepare for our display.
'* Hires (Hi resolution ? )
IF screen.width%>320 THEN
screen.mode% = 2
ELSE
screen.mode% = 1
END IF
'* Interlace (y = 0 - 399)
IF screen.height%>200 THEN screen.mode% = screen.mode%+2
'* ILBM depth% = 6 -> HAM/Halfbrite ?
IF ilbm.depth% = 6 THEN
ilbm.re% = -1
END IF
depth% = ilbm.depth%+ilbm.reg%
SCREEN 1,screen.width%,screen.height%,depth%,screen.mode%
WINDOW 2,,,0,1
'* System parameters
amiga.windo& = WINDOW(7)
amiga.screen& = PEEKL(amiga.windo&+46)
amiga.viewport& = amiga.screen&+44
amiga.rastport& = amiga.screen&+84
amiga.colormap& = PEEKL(amiga.viewport&+4)
amiga.colortable& = PEEKL(amiga.colormap&+4)
amiga.bitmap& = PEEKL(amiga.rastport&+4)
FOR loop1% = 0 TO ilbm.depth%-1
amiga.plane&(loop1%) = PEEKL(amiga.bitmap&+8+loop1%*4)
NEXT loop1%
'* For HAM/Halfbrite prepare 6 bitplanes.
IF ilbm.reg% = -1 THEN
ilbm.reg% = 0
newplane& = ALlocRaster&(screen.width%,screen.height%)
IF newplane& = 0 THEN
ilbm.error$ = "Not enough memory free for 6 bitplanes!"
ELSE
POKE amiga.bitmap&+5,6
POKEL amiga.bitmap&+28,newplane&
END IF
PAGE 286
---------------------------------------------------------------------------
END IF
;* ColorTable (CMAP)?
ELSE IF ilbm.ID.$ = "CMAP" THEN
'* REad chunk contents.
disk.wasread& = xRead&(disk.handle&,buf.read&,ilbm.chunk&)
status = status% OR 2^1
'* Calculate RGB table.
FOR loop1% = 0 TO screen.kolor% - 1
kolor.red% = PEEK(buf.read&+loop1%*3+0)
kolor.green%=PEEK(buf.read&+loop1%*3+1)
kolor.blue% =PEEK(buf.read&+loop1%*3+2)
kolor.rgb% = kolor.green%+16*kolor.red%+1/16*kolor.blue%
POKEW buf.rgb&+2*loop1%,kolor.rgb%
NEXT loop1%
'* Alignment.
IF (ilbm.chunk OR 1)=ilbm.chunk THEN
disk.wasread&=xRead&(disk.handle&,buf.read&,1)
END IF
'* Viewport Mode (CAMG) AMIGA ?
ELSE IF
ELSE IF ilbm.ID.$ = "CAMG" THEN
'* Read chunk contents.
disk.wasread& = xRead&(disk.handle&, buf.read&, ilbm.chunk&)
status% = status% OR 2^4
ccrt.direction% = PEEKW(buf.read&+0)
ccrt.start% = PEEK (buf.read&+2)
ccrt.end% = PEEK (buf.read&+3)
ccrt.secs& = PEEKL (buf.read&+4)
ccrt.mics& = PEEKL(buf.read&+8)
'* Bitplanes (BODY) ?
ELSEIF ilbm.ID.$ = "BODY" THEN
status% = status% OR 2^2
'* not compressed data.
IF ilbm.mode% = 0 THEN
FOR loop1% = 0 TO ilbm.height%-1
FOR loop2% = 0 TO ilbm.depth%-1
PAGE 287
---------------------------------------------------------------------------
screen.buffer& =amiga.plane&(loop2%)+(loop1%*
screen.bytes%)
disk.wasread& = xRead&(disk.handle&,screen.buffer&,
ilbm.bytes%)
NEXT loop2%
NEXT loop1%
'* Compressed Data (ByteRun1-Encoding)
ELSEIF ilbm.mode% = 1 THEN
FOR loop1% = 0 TO ilbm.height%-1
FOR loop2% = 0 TO ilbm.depth%-1
screen.buffer& = amiga.plane&(loop2%)+(loop1%*
screen.bytes%)
counter% = 0
'* Decoding.
WHILE counter% <ilbm.bytes%
disk.wasread& = xRead&(disk.handle&,buf.read&,1)
code% = PEEK(buf.read&)
'* Code 1: read n bytes uncoded.
IF code% < 128 THEN
disk.wasread& =
xRead&(disk.handle&,screen.buffer&+counter%,code%+1)
counter% = counter%+code%+1
'* Code 2 Repeat bytes (257-n) times.
ELSEIF code% > 128 THEN
disk.wasread& = xRead&(disk.handle&,buf.read&,1)
disk.byte% = PEEK(buf.read%)
FOR loop3% = counter% TO counter%+257-code%
POKE screen.buffer&+loop3%,disk.byte%
NEXT loop3%
counter% = counter%+257-code%
'* Code 3 : no operation.
ELSE
'* no operation.
END IF
WEND
NEXT loop2%
NEXT loop1%
'* Different decoding method.
ELSE
ilbm.error$ = "Data compression algorithm unknown. "
END IF
'* Process unimportant chunks. (GRAB, DEST, SPRT, etc...)
ELSE
;* Read straigh count bytes.
IF (ilbm.chunk% OR 1)=ilbm.chunk% THEN
ilbm.chunk%=lib.chunk%+1
PAGE 288
---------------------------------------------------------------------------
END IF
'*Move Disk cursor.
mode.current% = 0
stat& = Seek&(disk.handle&,ilbm.chunk%,0)
IF stat& = -1 THEN
ilbm.error = "DOS error. Seek() failed."
END IF
END IF
'* Error check.
IF disk.wasread& < - THEN
ilbm.error$ = "DOS Error. Read() Failed."
'* EOF (end of file) reached ?
ELSEIF disk.wasread& = 0 AND ((status% AND 7) <> 7 THEN
ilbm.error$ = "ILBM data chunks not present."
signal% = 1
ELSEIF (status% AND 7) = 7 THEN
signal% = 1
END IF
END SUB
SUB Hardcopy STATIC
mem.opt& = 2^0+2^16
p.io& = AllocMem&(100,mem.opt&)
p.port& = p.io&+62
IF p.io& = 0 THEN ERROR 7
f.windo& = WINDOW(7)
f.rastport& = PEEKL(f.windo&+50)
f.width% = PEEKW(f.windo&+112)
f.height% = PEEKW(f.windo&+114)
f.screen& = PEEKL(f.windo&+46)
f.viewport& = f.screen&+44
f.colormap& = PEEKL(f.viewport&+4)
f.vp.mode% = PEEKW(f.viewport&+32)
p.sigBit% = AllocSignal%(-1)
IF p.sigBit% = -1 THEN
PRINT "No Signalbit free!"
CALL FreeMem(p.io&,100)
EXIT SUB
END IF
p.sigTask& = FindTask&(0)
POKE p.port&+8,4
POKE p.port&+10,p.port&+34
POKE p.port&+15,p.sigBit%
POKEL p.port&+16,p.sigTask&
POKEL p.port&+20,p.port&+24
PAGE 289
---------------------------------------------------------------------------
POKEL p.port&+28,p.port&+20
POKE p.port&+34,ASC("P")
POKE p.port&+35,ASC("R")
POKE p.port&+36,ASC("T")
CALL AddPort(p.port&)
POKE p.io&+8,5
POKEL p.io&+14,p.port&
POKEW p.io&+28,11
POKEL p.io&+32,f.rastport&
POKEL p.io&+36,f.colormap&
POKEL p.io&+40,f.vp.mode%
POKEW p.io&+48,f.width%
POKEW p.io&+50,f.height%
POKEL p.io&+52,f.width%
POKEL p.io&+56,f.height%
POKEW p.io&+60,4
d.name$ = "printer.device"+CHR$(0)
status% = OpenDevice%(SADD(d.name$),0,p.io&,0)
IF status% <> 0 THEN
PRINT "Printer is not free":
CALL FreeMem(p.io&,100)
CALL FreeSignal(p.sigBit%)
EXIT SUB
END IF
ercond% = DoIO%(p.io&)
CALL CloseDevice(p.io&)
CALL RemPort(p.port&)
CALL FreeMem(p.io&,100)
CALL FreeSignal(p.sigBit%)
PRINT "Error Code: ";ercond%
END SUB
PROGRAM DESCRIPTION.
====================
The SUB program LoadILBM requires a string containing the name of the ILBM
picture you want to load from disk. The picture must be in the active disk
directory in order for the active program to find it (CHDIR directory). The
Extras diskette for workbench 1.2 contains the Heart.ILBM file, which is
a good example picture to use. This program loads and displays the picture.
Now you could call the SUB program ColorCycle. When this program finds a
CCRT ColorCycle data block, it activates the cycling, which creates a
motion like effect on the screen. This SUB program requires an integer
argument. If the argument is negative, the Amiga cycles the colours until
you press a key. If the argument is positive, the Amiga cycles the colors
for a time period determined by your argument. The SUB program ILBMend
stops the display and closes both the screen and window.
PAGE 290
------------------------------------------------------------------------
The DOS library routines are new in our program. You cannot use the built
in OPEN/INPUT#/CLOSE commands when working with ILBM files because these
commands place zeros in the data. Here is a short explanation of the DOS
routines used:
name$ = name$+CHR$(0)
disk.handle&=xOpen&(SADD(name$),1005)
name$ : Name of file to be opened.
1005 : ModeOldFile - file already exists.
1006 : ModeNewFile - make file with this name.
dis.handle& = BPTR (pointer/4) in handler data block when 0, then
xOpen failed.
=================================
wasread& = xRead(disk.handle&,buffer&,bytes&)
disk.handle& : Address from xOpen call.
buffer& : Address of a free memory area.
bytes& : Number of bytes to be read from the actual disk
cursor position that have to fit in the buffer!
wasread& : Number of bytes read.
=0: EOF (End Of File)
smaller than zero: read error.
======================================
oldpos& = Seek(disk.handle&,offset%,mode%)
disk.handle& : Address from xOpen call.
offset% : Number of bytes to move the disk cursor.
mode% : 0 = From current position.
-1 = From file begin.
1 = From file end.
======================================
CALL xClose(disk.handle&)
disk.handle& : Handle from xOpen command; closes file.
=======================================
CALL delay(ticks)
tick : 1/50/ second.
microseconds : 1/100000 second.
Waits for the specified time (however, this does not mean busy waiting,
the system has additional computing time free).
PAGE 291
---------------------------------------------------------------------------
Special Features of the program:
-------------------------------
This program not only supports the AmigaBASIC display modes hires and
interlace, but also the ILBM graphics in halfbrite (64 colors) and HAM mode
(4096) colours. Both modes use six bitplanes. Whenever one of these modes
is encountered, a sixth bitplane is added to the screen. However, we do not
clear this bitplane using FreeRaster. Instead, when the SCREEN CLOSE
statement closes the new screen, all the bitplanes, including the sixth
bitplane that was added are automatically deleted.
The program is capable of decoding compacted bitplanes by using the
"ByteRun1" method. This method uses two control codes. When the first byte
read is smaller than 128 then the following byte is simply loaded and used.
When the first byte is a value greater than 128, the second byte is
repeated 257 times (normally signed bytes from -127 to +128 are used for a
more complicated compression algorithm). When the byte is equal to 128
nothing happens because it has a NOP (No Operation).
PAGE 292
---------------------------------------------------------------------------
CHAPTER 8 - A 1024 * 1024 PAINT PROGRAM.
In the previous chapters you learned about the Amiga'a various system
components and how to program them. To conclude the BASIC portion of our
book, we have included a complete paint program that use superbitmap
layers. The program also includes the capability of using the Amiga fonts,
including their various modes and styles. Here is a brief description
for the program and what it can do:
* Full mouse and menu control.
* Up to 1024*1024 pixel sized drawings.
* Soft scrolling over the entire drawing area.
* Circles, lines, rectangles in the true rubberband technique.
* Freehand drawing.
* Text output in JAM1 and JAM2, complement and inverse.
* Up to 19 different fonts at the same time.
* Outline, italic, bold and underline text.
* Extensive Hardcopy functions.
* Printing of the entire 1024*1024 pixel graphic.
* Selected piece with enlargement/reduction.
* Distortion.
* Fill areas.
* Drawin grid.
* Block Erase.
* Copy a piece of the drawing.
* Self defined brushes and patterns.
Since the superbitmap planes are so large, this drawing program uses only
one bitplane. Because of this our drawings are limited to black and white.
We designed the program to create drawings that you can print. The large
drawing area makes it easy to create very detailed graphics that you can
then print on a graphics-capable printer in their original or a reduced
size.
PAGE 293
-----------------------------------------------------------------------------
'########################################
'#
'# Section : 8
'# Program : Superbitmap Paint Program.
'# Date : 04/16/87
'# Author : tob
'# Version : 1.0
'#
'########################################
PRINT "Searching for .bmap files. ....."
'GRAHPICS-library
DECLARE FUNCTION AskSoftStyle& LIBRARY
DECLARE FUNCTION SetSoftStyle& LIBRARY
DECLARE FUNCTION OpenFont& LIBRARY
DECLARE FUNCTION AllocRaster& LIBRARY
'EXEC-library
DECLARE FUNCTION DoIO& LIBRARY
DECLARE FUNCTION OpenDevice& LIBRARY
DECLARE FUNCTION AllocSignal& LIBRARY
DECLARE FUNCTION FindTask& LIBRARY
DECLARE FUNCTION AllocMem& LIBRARY
'DISKFONT-library
DECLARE FUNCTION OpenDiskFont& LIBRARY
DECLARE FUNCTION AvailFonts& LIBRARY
'LAYERS-library
DECLARE FUNCTION CreateBehindLayer& LIBRARY
DECLARE FUNCTION UpFrontLayer& LIBRARY
DECLARE FUNCTION BehindLayer& LIBRARY
LIBRARY "layers.library"
LIBRARY "grahpics.library"
LIBRARY "exec.library"
LIBRARY "intuition.library"
LIBRARY "diskfont.library"
setup: '* Here we go.
PRINT "Paint-1024 Drawing Program."
PRINT "---------------------------"
PRINT
PRINT "Do you want to work with LoRes(1) or HiRes(2) Screen."
PRINT "(has no effect on the size of the drawing area) ? "
PRINT
LINE INPUT "Enter Choice (1 or 2) ---> ";yn$
IF yn$ = "2" THEN
scrWidth% = 640
scrMode% = 2
ELSE
scrWidth% = 320
scrMode% = 1
END IF
initPar: '* SCreen parameters.
scrHeight% = 200
scrDepth% = 1
scrNr% = 1
PAGE 294
-----------------------------------------------------------------------------
WBenchScrNr% = -1
'* Window parameters.
windWidth% = scrWidth%-9
windHeight% = scrHeight%-26
windNr% = 1
windTitle$ = "Working Area"
windMode% = 16
'* Window Gadgets.
Xoffset% = 15
GadX% = windWidth%-Xoffset%+3
GadY% = 11
GadSX% = Xoffset%-3
GadSY% = GadSX%-1
GadNumber% = 5
GadTolerance%= 1
Gad$(0) = "^"
Gad$(1) = "v"
Gad$(2) = "<"
Gad$(3) = ">"
Gad$(4) = "H"
'* CAD superbitmap.
superWidth% = 800
superHeight% = 400
superFlag% = 4
'* Layer size.
layMinX% = 3
layMinY% =11
layMaxX% = winWidth%-8-Xoffset%
layMaxY% = windHeight%
'* Drawing modes
draw% = 4
mode$ = "FREEHAND"
drMd% = 0
style% = 0
swapper% = 1
kl% = 1
grid1% = 1
grid2% = 1
font.height% = 8
DIM get.array%(1)
'* Printer parameters.
printX0% = 0
printY0% = 0
printX1% = superWidth%
printY1% = superHeight%
printSpec% = 4
initDisp: '* Open screen and window.
SCREEN scrNr%,scrWidth%,scrHeight%,scrDepth%,scrMode%
WINDOW windNr%,windTitle$,(0,0)-(windWidth%,windHeight%),
windMode%,scrNr%
WINDOW OUTPUT windNr%
PALETTE 1,0,0,0
PALETTE 0,1,1,1
PAGE 295
-----------------------------------------------------------------------------
DIM area.pat%(3):DIM full%(1)
area.pat%(0) = &H1111
area.pat%(1) = &H2222
area.pat%(2) = &H4444
area.pat%(3) = &H8888
PATTERN ,area.pat%
PAINT (100,50),1,1
full%(0) = &HFFFF
full%(1) = full%(0)
PATTERN, full%
'* Prepar TmpRas.
buffersize& = superWidth%*superHeight%/8
buffer& = PEEKL(WINDOW(8)+12)
IF buffer&<>0 THEN
fillflag% = 1
mem& = PEEKL(buffer&)
size& = PEEKL(buffer&+4)
CALL FreeMem(mem&,size&)
opt& = 2^0+2^1+2^16
buf& = AllocMem&(buffersize&,opt&)
IF buf&=0 THEN
fillflag% = 0
POKEL WINDOW(8)+12,0
ELSE
POKEL buffer&,buf&
POKEL buffer&+4,buffersize&
END IF
ELSE
fillflag%=0
END IF
initsys: '* Read system parameters.
windAdd& = WINDOW(7)
scrAdd& = PEEKL(windAdd&+46)
scrViewPort&= scrAdd&+44
scrColMap& = PEEKL(scrViewPort&+4)
scrBitMap& = scrAdd&+184
scrLayerInfo&=scrAdd&+224
scrMode% = PEEKW(scrViewPort&+32)
font& = PEEKL(WINDOW(8)+52)
initSBmap: '* Create Superbitmap.
opt& = 2^0+2^1+2^16
superBitmap&= AllocMem&(40,opt&)
IF superBitmap& = 0 THEN
PRINT "mnn, not even 40 bytes?"
ERROR 7
END IF
'* Activate superbitmap
CALL InitBitMap(superBitmap&,scrDepth%,superWidth%,superHeight%)
superPlane& = AllocRaster&(superWidth%,superHeight%)
IF superPlane& = 0 THEN
PRINT "No room!"
CALL FreeMem(superBitmap&,40)
ERROR 7
END IF
POKEL superBitmap&+8,superPlane&
PAGE 296
-----------------------------------------------------------------------------
'* Open SuperBitMap layer.
SuperLayer& = CreateBehindLayer&(scrLayerInfo&, scrBitMap&,
layMinX%,layMinY%,layMaxX%,layMaxY%,superFlag%,superBitmap&)
IF SuperLayer& = 0 THEN
PRINT "Unable to create layer!"
CALL FreeRaster(superPlane&,superWidth%,superHeight%)
CALL FreeMem(superBitmap&,40)
ERROR 7
END IF
'* New Rastport.
SuperRast& = PEEKL(SuperLayer&+12)
initPrint: '* Printer initialisation.
opt& = 2^0+2^16
pio& = AllocMem&(100,opt&)
IF pio&<>0 THEN
port& = pio&+62
sigBit% = AllocSignal&(-1)
IF sigBit%<>-1 THEN
sigTask& = FindTask&(0)
POKE port&+8,4
POKEL port&+10,port&+34
POKE port&+15,sigBit%
POKEL port&+16,sigTask&
POKEL port&+20,port&+24
POKEL port&+28,port&+20
POKEL port&+34,1347572736&
CALL AddPort(port&)
POKE pio&+8,5
POKEL pio&+14,port&
POKEW pio&+18,12
POKEW pio&+28,11
POKEL pio&+32,SuperRast&
POKEL pio&+36,scrColMap&
POKEL pio&+40,scrMode%
POKEW pio&+48,superWidth%
POKEW pio&+50,superHeight%
POKEL pio&+52,superWidth%
POKEL pio&+56,superHeight%
POKEW pio&+60,4
ELSE
printflag% = 1
CALL FreeMem(pio&,100)
END IF
ELSE
printflag% = 1
END IF
prepare: '* Draw move gadgets.
CALL SetDrMd(WINDOW(8),5)
FOR loop% = 0 TO GadNumber%-1
LINE (GadX%,GadY%+(GadSY%+5)*loop%)-(GadX%+GadSX%,GadY%+
GadSY%+(GadSY%+5)*loop%),1,bf
gadMouse%(loop%) = GadY%+(GadSY%+5)*loop%-4-GadTolerance%
CALL Move(WINDOW(8),GadX%+3,GadY%+((GadSY%+5)*loop%)+8)
PRINT Gad$(loop%)
PAGE 297
-----------------------------------------------------------------------------
NEXT loop%
CALL SetDrMd(WINDOW(8),1)
'* Prepare drawing area.
CALL SetRast(SuperRast&,0)
'* Draw calibrations.
FOR loop% = 0 TO winWidth%-Xoffset% STEP 3
IF loop%/15= INT(loop%/15) THEN
LINE (loop%,windHeight%)-(loop%,windHeight%-10)
ELSE
LINE (loop%,windHeight%)-(loop%,windHeight%-5)
END IF
NEXT loop%
FOR loop% = 0 TO windHeight% STEP 2
IF loop%/10 = INT(loop%/10) THEN
LINE (winWidth%-Xoffset%,loop%)-(winWidth%-10-Xoffset%,
loop%)
ELSE
LINE (winWidth%-Xoffset%,loop%)-(winWidth%-5-Xoffset%,
loop%)
END IF
NEXT loop%
'* Display layer.
e& = UpFrontLayer&(scrLayerInfo&,SuperLayer&)
GOSUB newpointer
'* Protect and integrate layer.
POKEL SuperLayer&+40,windAdd&
backup.rast& = PEEKL(SuperLayer&+12)
backup.layer& = PEEKL(WINDOW(8))
POKEL SuperLayer&+12,WINDOW(8)
POKEL WINDOW(8),SuperLayer&
SuperRast& = WINDOW(8)
GOSUB coord
'* Menu control.
MENU 1,0,1,"Service"
MENU 1,1,1,"Clear Screen "
MENU 1,2,1,"Coordinates On "
MENU 1,3,1,"----------------"
MENU 1,4,1,"Transparent "
MENU 1,5,1,"JAM 2 "
MENU 1,6,1,"Complement "
MENU 1,7,1,"Inverse "
MENU 1,8,1,"----------------"
MENU 1,9,1,"normal/reset "
MENU 1,10,1,"italic "
MENU 1,11,1,"bold "
MENU 1,12,1,"underline "
MENU 1,13,1,"outline "
MENU 1,14,1,"----------------"
MENU 1,15,1,"s/w -> w/s "
MENU 1,16,1,"Title bar on/off"
MENU 1,17,1,"QUIT "
MENU 2,0,1,"Draw"
MENU 2,1,1,"Circle "
MENU 2,2,1,"Square "
MENU 2,3,1,"Lines "
MENU 2,4,1,"FreeHand "
PAGE 298
-----------------------------------------------------------------------------
MENU 2,5,1,"Text "
MENU 2,6,1,"Erase "
MENU 2,7,fillflag%,"Fill "
MENU 2,8,1,"Raster/Grid"
MENU 2,9,1,"Grid Reset"
MENU 2,10,1,"Get Area "
MENU 2,11,1,"Paint Area "
MENU 3,0,1,"Font"
MENU 3,1,1,"Load Fonts"
MENU 4,0,1,"I/O"
MENU 4,1,1,"Print "
MENU 4,2,1,"Param. "
ON MENU GOSUB MenuCtrl
MENU ON
mcp: '* Master Control Program.
WHILE forever=forever
test% = MOUSE(0)
mx% = MOUSE(1)
my% = MOUSE(2)
GOSUB updateDisp
CALL SetDrMd(SuperRast&,drMd%)
enable% = AskSoftStyle&(SuperRast&)
n& = SetSoftStyle&(SuperRast&,style&,enable%)
'* Drawing.
IF mx%>layMinX% AND mx%<layMaxX% AND test%<0 THEN
IF draw% = 4 THEN
GOSUB freedraw
ELSEIF draw% = 10 THEN
GOSUB paintdraw
ELSEIF draw% = 5 THEN
GOSUB drawtext
ELSEIF draw% = 7 THEN
GOSUB filler
ELSE
GOSUB drawit
IF draw%=3 AND fetch% = 1 THEN
printX0% = cX%+subox%
printX1% = 1+cX%+subox%+oldrcX%
printY0% = cY%+suboy%
printY1% = 1+cY%+suboy%+oldrcY%
GOTO continue
ELSEIF draw% = 3 AND grid% = 1 THEN
x1% = cX%+subox%
y1% = cY%+suboy%
x2% = cX%+subox%+oldrcX%
y2% = cY%+suboy%+oldrcY%
IF x1%>x2% THEN SWAP x1%,x2%
IF y1%>y2% THEN SWAP y1%,y2%
g.width% = x2%-x1%
g.height% = y2%-y1%
IF copy% = 0 THEN
grid1% = g.width%
grid2% = g.height%
ELSE
g.size&+6+(g.height%+1)*2*INT(g.width%+16)/16)
IF g.size&>(FRE(0)-1000) THEN
BEEP
ELSE
PAGE 299
-----------------------------------------------------------------------------
ERASE get.array%
DIM get.array%(g.size&/2)
GET (x1%,y1%)-(x2%,y2%),get.array%
END IF
END IF
END IF
END IF
ELSE IF (test%<0 AND mx%>layMaxX%) THEN
'* Scroll gadgets in use ?
IF my%>gadMouse%(4) THEN
GOSUB ScrollHome
ELSEIF my%>gadMouse%(3) THEN '<-
GOSUB ScrollLeft
ELSEIF my%>gadMouse%(2) THEN '->
GOSUB ScrollRight
ELSEIF my%>gadMouse%(1) THEN 'up
GOSUB ScrollUp
ELSEIF my%>gadMouse%(0) THEN 'down
GOSUB ScrollDown
END IF
END IF
WEND
deleteSys: '* remove system
buf& = PEEKL(WINDOW(8)+12)
IF buf&<>0 THEN
buffer& = PEEKL(buf&)
size& = PEEKL(buf&+4)
CALL FreeMem(buffer&,size&)
POKEL WINDOW(8)+12,0
END IF
IF ptr&<>0 THEN
CALL ClearPointer(WINDOW(7))
CALL FreeMem(ptr&,20)
END IF
POKEL SuperLayer&+12,backup.rast&
POKEL WINDOW(8),backup.layer&
POKEL SuperLayer&+40,0
CALL DeleteLayer(scrLayerInfo&,SuperLayer&)
CALL FreeRaster(superPlane&,superWidth%,superHeight%)
CALL FreeMem(superBitmap&,40)
SCREEN CLOSE scrNr%
WINDOW windNr%,"hi!",,,WBenchScrNr%
IF printflag%<>1 THEN
CALL RemPort(port&)
CALL FreeSignal(sigbit%)
CALL FreeMem(pio&,100)
END IF
IF oldFont&<>0 THEN CALL CloseFont(oldFont&)
LIBRARY CLOSE
END
'**** That was it. Here are the important subroutines. ****
MenuCTrl: '* Menu in use.
menuId = MENU(0)
menuItem = MENU(1)
PAGE 300
------------------------------------------------------------------------------
IF menuId = 1 THEN
IF menuItem = 1 THEN
CALL SetRast(SuperRast&,0)
ELSEIF menuItem = 2 THEN
GOSUB coord
ELSEIF menuItem = 4 THEN
drMd% = 0
ELSEIF menuItem = 5 THEN
drMd% = drMd% OR 1
ELSEIF menuItem = 6 THEN
drMd% = drMd% OR 2
ELSEIF menuItem = 7 THEN
drMd% = drMd% OR 4
ELSEIF menuItem = 9 THEN
style%=0:drMd%=0:outline%=0
ELSEIF menuItem = 10 THEN
style% = style% OR 4
ELSEIF menuItem = 11 THEN
style% = style% OR 2
ELSEIF menuItem = 12 THEN
style% = style% OR 1
ELSEIF menuItem = 13 THEN
outline% = 1
ELSEIF menuItem = 15 THEN
GOSUB swapcol
ELSEIF menuItem = 16 THEN
IF k1% = 0 THEN
k1% = 1
ELSE
k1% = 0
END IF
ELSEIF menuItem = 17 THEN
GOTO deleteSys
END IF
ELSEIF menuId = 2 THEN
grid% = 0
copy% = 0
IF menuItem = 1 THEN
mode$ = "CIRCLE"
draw% = 1
ELSEIF menuItem = 2 THEN
mode$ = "SQUARE"
draw% = 3
ELSEIF menuItem = 3 THEN
mode$ = "LINES"
draw% = 2
ELSEIF menuItem = 4 THEN
mode$ = "FREEHAND"
draw% = 4
ELSEIF menuItem = 5 THEN
mode$ = "TEXT"
draw% = 5
ELSEIF menuItem = 6 THEN
mode$ = "DELETE"
draw% = 6
ELSEIF menuItem = 7 THEN
mode$ = "FILL"
draw% = 7
ELSEIF menuItem = 8 THEN
mode$ = "GRID"
PAGE 301
-----------------------------------------------------------------------------
grid% = 1
draw% = 3
ELSEIF menuItem = 9 THEN
grid1% = 1
grid2% = 1
ELSEIF menuItem = 10 THEN
mode$ = "GET AREA"
draw% = 3
grid% = 1
copy% = 1
ELSEIF menuItem = 11 THEN
mode$ = "PAINT"
draw% = 10
END IF
ELSEIF menuId = 3 THEN
IF fontflag% = 0 THEN
GOSUB loadFonts
ELSE
GOSUB loadFont
END IF
ELSEIF menuId = 4 THEN
IF menuItem = 1 THEN
IF printflag%<>1 THEN
GOSUB hardcopy
ELSE
BEEP
END IF
ELSEIF menuItem=2 THEN
GOSUB changePrint
END IF
END IF
IF k1% = 1 THEN
tboff$ = mode$+" / Titlebar Off"+CHR$(0)
CALL WaitTOF
CALL SetWindowTitles(windAdd&,SADD(tboff$),-1)
END IF
RETURN
coord: '* Show coordinates intersection.
CALL SetDrMd(WINDOW(8),2)
POKEW SuperRast&+34,&HAAAA
FOR loop% = 0 TO superWidth% STEP 50
LINE (loop%,0)-(loop%,superHeight%)
NEXT loop%
FOR loop%=0 TO superHeight% STEP 50
LINE (loop%,0)-(superWidth%,loop%)
NEXT loop%
POKEW SuperRast&+34,&HFFFF
CALL SetDrMd(WINDOW(8),drMd%)
RETURN
drawit: '* Multifunction Drawing with rubberbanding.
cX% = MOUSE(1)
cY% = MOUSE(2)
test% = MOUSE(0)
mx%=1:my%=1
ccX%=0:ccY%=0
oldcX%=0:oldcY%=0
rcX%=0:rcY%=0
oldrcX%=0:oldrcY%=0
PAGE 302
-----------------------------------------------------------------------------
CALL SetDrMd(SuperRast&,2)
subox% = ox%
suboy% = oy%
loopflag% = 0
IF (cX% MOD grid1%) > (.5*grid1%) THEN cX%=cX%+grid1%
IF (cY% MOD grid2%) > (.5*grid2%) THEN cY%=cY%+grid2%
cX% = cX% - (cX% MOD grid1%)
cY% = cY% - (cY% MOD grid2%)
GOSUB oldpos
WHILE test% < 0
test%=MOUSE(0)
oldx%=mx%
oldy%=my%
oldcX%=ccX%
oldCy%=ccY%
oldrcX%=rcX%
oldrcY%=rcY%
mx% = MOUSE(1)
my% = MOUSE(2)
IF (mx% MOD grid1%) > (.5*grid1%) THEN mx% = mx%+grid1%
IF (my% MOD grid2%) > (.5*grid2%) THEN my% = my%+grid2%
mx% = mx% - (mx% MOD grid1%)
my% = my% - (my% MOD grid2%)
IF mx%=oldx% AND my%=oldy% AND s.fl%=0 THEN
rep.flag% = 1
ELSE
rep.flag% = 0
s.fl% = 0
END
IF rep.flag% = 0 THEN
GOSUB oldpos
END IF
IF mx%<layMinX%+5 THEN GOSUB ScrollRight
IF mx%>layMaxX%-15 THEN GOSUB ScrollLeft
IF my%<layMinY%+5 THEN GOSUB ScrollDown
IF my%>layMaxY%-20 THEN GOSUB ScrollUp
GOSUB updateDisp
ccX%=ABS(mx%-cX%) + ABS(ox%-subox%)
ccY%=ABS(my%-cY%) + ABS(oy%-suboy%)
rcY% = my%-cY%+(oy%-suboy%)
rcX% = mx%-cX%+(ox%-subox%)
IF rep.flag%=0 THEN
GOSUB newpos
END IF
WEND
GOSUB newpos
CALL SetDrMd(SuperRast&,1)
IF draw% = 6 THEN
x1% = cX%+subox%
y1% = cY%+suboy%
x2% = cX%+subox%+oldrcX%
y2% = cY%+suboy%+oldrcY%
IF x2%<x1% THEN SWAP x1%,x2%
IF y2%<y1% THEN SWAP y1%,y2%
x1%=x1%+1:y1%=y1%+1
PAGE 303
-----------------------------------------------------------------------------
x2%=x2%-1:y2%=y2%-1
CALL SetAPen(WINDOW(8),0)
CALL RectFill(WINDOW(8),x1%,y1%,x2%,y2%)
CALL SetAPen(WINDOW(8),1)
ELSEIF (draw%=3 AND (fetch%<>0 OR grid%<>0)) THEN
REM nothing
ELSE
GOSUB newpos
END IF
RETURN
newpos:
IF draw%=1 THEN
CALL DrawEllipse(SuperRast&,cX%+subox%,cY%+suboy%,ccX%,ccY%)
ELSEIF draw%=2 THEN
LINE (cX%+subox%,cY%+suboy%)-(cX%+subox%+rcX%,cY%+suboy%+
rcY%),swapper%
ELSEIF draw% = 3 OR draw% = 6 THEN
LINE (cX%+subox%,cY%+suboy%)-(cX%+subox%+rcX%,cY%+suboy%+
rcY%),swapper%,b
END IF
RETURN
oldpos:
IF draw% = 1 THEN
CALL DrawEllipse(SuperRast&,cX%+subox%,cY%+suboy%,oldcX%,
oldcY%)
ELSEIF draw%=2 THEN
LINE (cX%+subox%,cY%+suboy%)-(cX%+subox%+oldrcX%,cY%+suboy%
+oldrcY%),swapper%
ELSEIF draw%=3 OR draw%=6 THEN
LINE (cX%+subox%,cY%+suboy%)-(cX%+subox%+oldrcX%,cY%+suboy%
+oldrcY%),swapper%,b
END IF
RETURN
filler: '* Fill routine
test% = MOUSE(0)
oldx% = MOUSE(1)
oldy% = MOUSE(2)
PAINT (ox%+oldx%,oy%+oldy%),1,1
RETURN
freedraw: '* Freehand drawing.
test% = MOUSE(0)
oldx% = MOUSE(1)
oldy% = MOUSE(2)
WHILE test% < 0
oldx% = mx%
oldy% = my%
mx% = MOUSE(1)
my% = MOUSE(2)
IF mx%<layMinX%+10 THEN GOSUB ScrollRight
IF mx%>layMaxX%-20 THEN GOSUB ScrollLeft
IF my%>layMinY%+10 THEN GOSUB ScrollDown
IF my%<layMaxY%-25 THEN GOSUB ScrollUp
LINE (ox%+oldx%,oy%+oldy%)-(ox%+mx%,oy%+my%),swapper%
GOSUB updateDisp
test% = MOUSE(0)
WEND
RETURN
PAGE 304
-----------------------------------------------------------------------------
paintdraw: '*Draw with image.
test% = MOUSE(0)
WHILE test%<0
mx% = MOUSE(1)
my% = MOUSE(2)
IF mx%<layMinX%+10 THEN GOSUB ScrollRight
IF mx%>layMaxX%-20 THEN GOSUB ScrollLeft
IF my%<layMinY%+10 THEN GOSUB ScrollDown
IF my%>layMaxY%-25 THEN GOSUB ScrollUp
mx% = mx%-(mx% MOD grid1%)
my% = my%-(my% MOD grid2%)
IF mx%<layMinX%+10 THEN GOSUB ScrollRight
IF mx%>layMaxX%-20 THEN GOSUB ScrollLeft
IF my%<layMinY%+10 THEN GOSUB ScrollDown
IF my%>layMaxY%-25 THEN GOSUB ScrollUp
test% = MOUSE(0)
PUT (mx%+ox%,my%+oy%),get.array%,OR
WEND
RETURN
scrollhome:
x% = -ox%
y% = -oy%
oy%= 0
ox%= 0
GOSUB ScrollDisplay
RETURN
ScrollRight:
IF ox%grid1% THEN
x% = -grid1%
ox% = ox%-grid1%
GOSUB ScrollDisplay
END IF
RETURN
ScrollLeft:
IF ox%<(superWidth%-layMaxX%+layMinX%-grid1%) THEN
x% = grid1%
IF textWidth% <> 0 THEN
IF ox%+textWidth%<(superWidth%-layMaxX%+layMin%) THEN
x% = textWidth%
END IF
textwidth% = 0
END IF
ox% = ox%+x%
GOSUB ScrollDisplay
END IF
RETURN
scrollUp:
IF oy%<(superHeight%-layMaxY%+layMinY%-grid2%) THEN
y% = grid2%
oy%= oy%+grid2%
GOSUB ScrollDisplay
END IF
RETURN
scrollDown:
IF oy%>grid2%-1 THEN
y% = -grid2%
oy%= oy%-grid2%
GOSUB ScrollDisplay
PAGE 305
-----------------------------------------------------------------------------
END IF
RETURN
ScrollDisplay:'* scroll it
CALL ScrollLayer(scrLayerInfo&,SuperLayer&,x%,y%)
x% = 0
y% = 0
s.fl%=1
RETURN
updateDisp:
IF k1% = 0 THEN
actu$="> "+mode$+" [F]="+STR$(fontHeight%)+" [X]="+STR$
(ox%+mx%)+" [Y]="+STR$(oy%+my%)+CHR$(0)
CALL WiatTOF
CALL SetWindowTitles(winAdd&,SADD(actu$),-1)
END IF
RETURN
loadFonts: '* Load disk fonts.
sp$ = mode$
mode$ = "LOAD FONTS"
GOSUB upddateDisp
opt& = 2^0+2^16
bufLen& = 3000
buffer& = AllocMem&(bufLen&,opt&)
IF buffer&<>0 THEN
er& = AvailFonts(buffer&,bufLen&,3)
IF er& = 0 THEN
fontcnt% = PEEKW(buffer&)
IF fontcnt% > 19 THEN fontcnt% = 19
DIM textAttr&(fontcnt%*2)
DIM textName$(fontcnt%)
FOR loop% = 0 TO fontcnt%-1
counter% = loop%*10
fontTitle& = PEEKL(buffer&+4+counter%)
fontH% = PEEKW(buffer&+counter%+8)
textAttr&(loop%*2+1)= PEEKL(buffer&+counter%+8)
fontTitle$ = ""
check% = PEEK(fontTitle&)
WHILE check%<>ASC(".")
fontTitle$=fontTitle$+CHR$(check%)
fontTitle&= fontTitle&+1
check% = PEEK(fontTitle&)
WEND
textName$(loop%) = fontTitle$+".font"+CHR$(0)
fontName$ = fontcounter+1
MENU 3,fontcounter,1,fontName$
NEXT loop%
CALL FreeMem(buffer&,buffLen&)
fontflag% = 1
END IF
ELSE
BEEP
END IF
mode$ = sp$
RETURN
loadFont: '* Load font.
PAGE 306
-----------------------------------------------------------------------------
sp$ = mode$
mode$ = "LOAD FONT"
GOSUB updateDisp
textBase% = (menuItem-1)*2
textAttr&(textBase%) = SADD(textName$(menuItem-1))
newFont& = OpenDiskFont&(VARPTR(textAtrr&(textBase%)))
IF newFont& = 0 THEN
newFont& = OpenFont&(VARPTR(textAttr&(textBase%)))
END IF
IF newFont& <> 0 THEN
IF oldFont& <> 0 THEN
CALL CloseFont(oldFont&)
END IF
CALL SetFont(SuperRast&,newFont&)
oldFont& = newFont&
fontHeight% = INT(textAttr%(textBase%+1)/2^16)
ELSE
BEEP
END IF
mode$ = sp$
RETURN
drawtext: '* Write text in graphic bitmap
IF (mx% MOD grid1%) > (.5*grid1%) THEN mx%=mx%+grid1%
IF (my% MOD grid2%) > (.5*grid2%) THEN my%=my%+grid2%
my%=my%-(my% MOD grid2%)
mx%=mx%-(mx% MOD grid1%)
CALL Move(SuperRast&,mx%+ox%,my%+oy%+fontHeight%)
mode$ = "ENTRY"+CHR$(0)
CALL WaitTOF
CALL SetWindowTitles(windAdd&,SADD(mode$),-1)
mode$ = "TEXT"
in$ = ""
WHILE in$<> CHR$(13)
IF in$<>"" THEN
CALL SetDrMd(SuperRast&,drMd%)
enable% = AskSoftStyle&(SuperRast&)
n& = SetSoftStyle&(SuperRast&,style%,enable%)
tempX% = PEEKW(SuperRast&+36)
tempY% = PEEKW(SuperRast&+38)
rand% = tempX%-ox%
IF rand%>layMaxX%-20 THEN
textWidth% = PEEKW(SuperRast&+60)
GOSUB ScrollLeft
END IF
IF outline% = 0 THEN
CALL Text(SuperRast&,SADD(in$),1)
ELSE
CALL SetDrMd(SuperRast&,0)
FOR loop1% = -1 TO 1
FOR loop2% = -1 TO 1
CALL Move(SuperRast&,tempX%+loop2%,tempY%+
loop1%)
CALL Text(SuperRast&,SADD(in$),1)
PAGE 307
-----------------------------------------------------------------------------
NEXT loop2%
NEXT loop1%
CALL SetDrMd(SuperRast&,2)
CALL Move(SuperRast&,tempX%,tempY%)
CALL Text(SuperRast&,SADD(in$),1)
tempW% = 0
END IF
END IF
in$ = INKEY$
'*Function key assignments.
'* These lines could be used to assign characters
'* which are normally unaccessable to the function
'* keys for use in TEXT mode
IF in$ = CHR$(129) THEN in$ = CHR$(49)
IF in$ = CHR$(130) THEN in$ = CHR$(50)
IF in$ = CHR$(131) THEN in$ = CHR$(51)
IF in$ = CHR$(132) THEN in$ = CHR$(52)
IF in$ = CHR$(133) THEN in$ = CHR$(53)
IF in$ = CHR$(134) THEN in$ = CHR$(54)
IF in$ = CHR$(135) THEN in$ = CHR$(55)
IF in$ = CHR$(136) THEN in$ = CHR$(56)
IF in$ = CHR$(137) THEN in$ = CHR$(57)
IF in$ = CHR$(138) THEN in$ = CHR$(48)
WEND
m$="TEXT"+CHR$(0)
CALL WaitTOF
CALL SetWindowTitles(windAdd&,SADD(m$),-1)
mx% = 0
my% = 0
RETURN
newpointer: '* Define Drawing Pointer.
opt& = 2^1+2^16
ptr& = AllocMem&(20,opt&)
IF ptr&<>0 THEN
POKEW ptr&+4,256
POKEW ptr&+8,640
POKEW ptr&+12,256
CALL SetPointer(WINDOW(7),ptr&,3,16,-8,-1)
END IF
RETURN
hardcopy: '* Print bitmap
sp$ = mode$
mode$ = "HARDCOPY"
GOSUB updateDisp
dev$ = "printer.device"+CHR$(0)
er& = OpenDevice&(SADD(dev$),0,pio&,0)
IF er& = 0 THEN
er& = OpenDevice&(SADD(dev$),0,pio&,0)
er& = DoIO&(pio&)
IF er&<>0 THEN BEEP:BEEP
CALL CloseDevice(pio&)
ELSE
BEEP
END IF
mode$ = sp$
RETURN
swapcol:
IF swapper% = 0 THEN
swapper% = 1
ELSE
PAGE 308
-----------------------------------------------------------------------------
swapper% = 0
END IF
RETURN
changePrint: '* Change printer parameters
'* Output to your window.
backup.font& = PEEKL(WINDOW(8)+52)
CALL SetFont(WINDOW(8),font&)
POKEL SuperLayer&+12,backup.rast&
POKEL WINDOW(8),backup.layer&
e& = BehindLayer&(scrLayerInfo&,SuperLayer&)
CALL SetDrMd(WINDOW(8),1)
LINE (0,0)-(windWidth%-8-offset%-20,windHeight%-15),1,bf
LINE (20,10)-(windWidth%-offset%-40,windHeight%-25),0,bf
LOCATE 3,1
PRINT TAB(4);"PRINT PARAMETER SETTINGS"
PRINT TAB(4);"------------------------"
PRINT
PRINT TAB(4);"Outline area to be printed"
PRINT TAB(4);"with the frame"
PRINT
FOR t=1 TO 10000:NEXT t
repeat:
fetch% = 1
draw% = 3
mode% = "FETCH"
'* output back to the layer *'
e& = UpFrontLayer&(scrLayerInfo&,SuperLayer&)
POKEL SuperLayer&+12,WINDOW(8)
POKEL WINDOW(8),SuperLayer&
GOTO mcp
continue:
fetch% = 0
mode$ = "SQUARE"
'* Output on your window .
e& = BehindLayer&(scrLayerInfo&,SuperLayer&)
POKEL SuperLAyer&+12,backup.rast&
POKEL WINDOW(8),backup.layer&
LOCATE 9,1
PRINT TAB(4); USING "New Starting X:####";printX0%
PRINT TAB(4); USING "New Starting Y:####";printY0%
PRINT TAB(4); USING "New Ending X:####";printX1%
PRINTX1P%= printX1% - printX0%
PRINT TAB(4); USING "New Ending Y:####";printY1%
PRINTY1P%= printY1% - printY0%
LOCATE 15,1
PRINT TAB(4) SPACE$(26)
LOCATE 15,1
PRINT TAB(4);
INPUT "Are the values OK (y/n) ? ";yn$
PAGE 309
-----------------------------------------------------------------------------
IF yn$ = "n" THEN GOTO repeat
PRINT TAB(4);
INPUT "[1] Normal [2] Resised";nv%
IF nv% = 2 THEN
PRINT TAB(4);
INPUT "Absolute X Expansion";printX%
PRINT TAB(4);
INPUT "Absolute Y Expansion";printY%
printSpec% = 0
ELSE
printSpec% = 4
END IF
POKEW pio&+44,printX0%
POKEW pio&+46,printY0%
POKEW pio&+48,printX1P%
POKEW pio&+50,printY1P%
POKEL pio&+52,printX%
POKEL pio&+56,printY%
POKEW pio&+60,printSpec%
'* Output back to the layer.
e& = UpFrontLayer&(scrInfo&,SuperLayer&)
POKEL SuperLayer&+12,WINDOW(8)
CALL SetFont(WINDOW(8),backup.font&)
CALL SetDrMd(WINDOW(8),drMd%)
RETURN
PAGE 310
------------------------------------------------------------------------
8.1 PAINT 1024 PROGRAM INSTRUCTIONS.
Before you start the program, take a good look at the variable definitions
at the beginning. You can use either lo-res or hi-res for your working
screen. Since this has no effect on the size of the drawing we recommend
you use the lower resolution.
You can choose the size of the superbitmap which means that you will be
choosing the overall size of the drawing. The current program settings
define a 400*800 pixel drawing area. When you have enough memory, you can
expand this to the full 1024*1024 CAD standard.
Starting the Program:
--------------------
You start paint-1024 with "RUN". If you have two disk drives put your
program in one drive and the Workbench disk in the second drive. For use
with single drives start the program and then replace the program disk with
the Workbench disk. If you receive the message "Please put XXX in drive..."
you are returned to the workbench screen. To get back to the program screen
press the left Amiga <A> key and the <M> key simultaneously. Or you can
simply slide the workbench screen down.
First Drawing :
---------------
Now you are in the drawing program. The drawing area is the biggest portion
of the screen. The title bar is switched off as soon as the program begins.
Press the right mouse button once and the menu bar appears. The SERVICE
menu contains:
___________________________________________________________________
| SERVICE DRAW FONT I/O |
-------------------------------------------------------------------
| Clear Screen |
| Coordinates On |
| -------------- |
| Transparent |
| JAM 2 |
| Complement |
| Inverse |
| -------------- |
| Normal/reset |
| Italic |
| Bold |
| Underline |
| Outline |
| -------------- |
| s/w -> w/s |
| Title Bar |
| On/Off |
| QUIT |
|________________|
PAGE 311
------------------------------------------------------------------------
The first menu selection clears the entire drawing. The second selection
switches on a coordinate grid. If you select this item a second time the
grid is removed.
The next nine items determine the method of text display, which we will
discuss shortly. The s/w and w/s switch exchanges the back and foreground
colours. This can be very helpful when you want to delete only part of
your drawing.
Try selecting "Titlebar on/Off" once. The actual mouse position will be
displayed in the title bar along with the active drawing mode and the
height of the current font. The title bar requires a lot of calculation
time to keep it updated. Also, the scrolling and drawing functions will
slow down when the title bar displays the mouse coordinates. So, when you
can do without the title bar leave it switched off.
When the program starts you will be in FREEHAND drawing mode. As soon as
you press the left mouse button you will be drawing. When you get close to
the right or lower border, the screen begins scrolling. Additional portions
of the superbitmap become visible if you are not already at a border of the
plane.
In the right screen border you will find five small icons. Move onto one of
them with the mouse and press the left button. The screen scrolls in the
direction indicated by the arrow if there is anything to move. The H
symbol stands for Home and, with incredible speed, will return the drawing
to it's original position.
Circles, Lines and Squares :
----------------------------
Under the menu item DRAW are the different drawing functions. As long as
you hold down the left mouse button you can set the size and direction of
the selected drawin function. Releasing the button draws the object.
Please remember that the Circle command only functions with Kickstart
Version 1.2 or higher.
Erase a Portion of the Drawing :
--------------------------------
To delete only a portion of your drawing select ERASE. You can then select
a rectangular portion of your drawing that will be replaced with the
background colour.
Text Output :
-------------
Use this option to add the text to your drawing. After you activate the
TEXT mode you can still move around with the mouse. When you press the left
mouse button the Amiga expects text to be entered through the keyboard. To
end text entry, press the <Enter> key. The function keys are currently
assigned to the numbers 0 thru 9. They can be reassigned in the program to
contain special characters of the Amiga fonts.
PAGE 312
--------------------------------------------------------------------------
The SERVICE menu provides you with many text modes:
Transparent: Text does not delete graphics.
JAM2 : Text overwrites graphics.
Complement : What is black becomes white and vice versa.
Inverse : Back and foreground colours are swapped (only functions
in normal text mode).
Normal : All text styles are switched off.
Italic : Slanted Text.
Bold : Bold text.
Outline : Silhouette text.
The text styles can be mixed by selecting more than one style. It is also
possible to change styles while you are in text output mode.
Pressing <Enter> ends the text output mode.
If you want to enter more than one line of text and align the entire text,
switch on the GRID. Select the X direction width of one character of the
font and the Y direction height (more information about GRID follows).
End each line with <Enter>. To start the next line, move the mouse to the
desired position and press the left button.
Using Different Fonts :
-----------------------
The FONT menu allows you to use disk fonts for your text output. The first
time you use this menu item you will see LoadFonts. Before you select
this menu item you should insert the workbench disk that contains your
fonts into one of your drives. When LoadFonts is selected it will search
for all the usable fonts (19 is the maximum number the menu will handle).
The next time you click this menu item, it will contain a list of
available fonts. You can select any font from this list. The TEXT item of
the DRAW menu uses the last selected font.
Graphic Font :
--------------
If you have a graphic-capable printer, you can print out your drawing.
The I/O menu contains the PRINT and PARAM options. To print the entire
drawing plane, simply select PRINT. If you want to print only a portion
of the drawing, select Param. This menu item allows you to select a section
of your drawing by using a rectangular-rubberband method that frames the
area you want printed with a rectangle. The selected area is displayed and
you can make any needed corrections. Once the section is correct, you can
choose between (1) Normal Print and (2) Distorted print. Normal print
produces the graphic in its normal proportions. Distorted print allows you
to specify the number of pixels in X and Y directions, that the printed
output should use. If you specify more pixels in the horizontal direction
than your printer is able to print, then nothing will be printed.
PAGE 313
---------------------------------------------------------------------------
During printing all graphic functions are switched off.
Raster/Grid :
-------------
Often, it is necessary to create symmetrical drawings or diagrams. The
Rastre/Grid menu allows you to create a drawing grid of any size. Simply
selet the grid square size that you need. From this point on, all drawing
operations will function according to this grid. You can change this grid
size at any time. GRID RESET returns the grid to a 1*1 pixel size which is
the default drawing mode.
Area Fill :
-----------
The FILL function lets you fill areas of any size. First the area to be
filled must be framed completely with a black line that has no gaps. Place
the mouse in the middle of the area to be filled and press the left mouse
button. When filling in very large areas, and when working with large
objects in the drawing area, this operation can take over a minute to
complete.
User Defined Brushes :
----------------------
You can select a piece of your drawing to use as a brush. The size of th
brush is limited only by the available memory. To do this, select GET AREA
from the DRAW menu. Now you can frame the desired area and "grab it". You
can draw with this area by using Paint Area.
User Defined Patterns :
-----------------------
The way patterns work is quite similar to brush. You can define any portion
of your graphic as a pattern. To do this, draw a small sample of the pattern
and grab it using Get Area. Now grab the same piece again using the Raster
/ Grid menu item and then select Paint Area. Now you can use your pattern
as a brush to paint on the screen.
PAGE 314
--------------------------------------------------------------------------
GRAPHIC PROGRAMMING IN 'C'
==========================
You have probably noticed that although BASIC can create fantastic graphics,
it is quite slow. It is obvious that BASIC just doesnt have the speed that
we need to create fast graphics. However, if we use machine code, we could
gain more speed, but machine language can be difficult to program.
The high level language 'C' is perfct for this type of programming. This
language offers the speed usually attained only with machine language and
the simplicity of a programming language like BASIC.
Since we do not want to repeat the infomation provided in the first section
of this book, in this chapter, we will only discuss the C characteristics
for graphic programming.
The programs in this section were written using three compilers. Aztec C
V3.6a, Lattice C V5 and one using the Lattice C V3.10 compiler. Minor
changes may have to be made to compile the prorams on different compilers.
See you compiler documentation for more information. Please remember to
raise the system stack to 1024 bytes (CLI Command: stack 1024). Now we will
present the required elements for graphic programming in 'C'.
PAGE 315
----------------------------------------------------------------------------
The Amiga Libraries.
====================
Since learning a new computr language can be difficult, we will concentrate
only on graphic programming with C and not on learning the C langauge. If
certain terms such as Cast and Struct are confusing, please refer to the
Abacus book, Amiga C for Beginners (** Complete Book is one *-ALLIANCE-* Dox
Disk One !!...hehehe) or another book on Amiga C.
Before you can use C's graphic functions, which are not part of the standard
C functions, first you have to learn about the Amiga libraries.
Each library contains a series of jump addresses for the individual graphic
ROM routines. Since there are different ROM versions (kickstarts) for the
Amiga, the library concept is very useful. Every library has its own memory
section that places a routine at a specific address. This means that a
routine can be at a different address in each KickStart version. These
addresses are found in the libraries. This allows a program to work with
Kickstart 1.2 and 1.3 (and now 2.0) even though the routines are in
different locations.
There are libraries for many different purposes. For us, the most important
library is the graphics library. You open the graphics library like this:
GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0)
GfxBase is the structure that contains all the important library
information. The OpenLibrary funtion passes a pointer for this information
to an initialised GfxBase structure that must be named GfxBase. At the same
time, the string "graphics.library" is the name of the graphics library.
The zero in the parameter list for OpenLibrary specifies the current version
of the library, which is the one that we want to open.
Now that we have opened the graphics library we can return to graphic
programming. First we must create an area where we can display and save
our graphics.
Let's compare our operations to those of a large factory.
PAGE 316
-----------------------------------------------------------------------------
9.2 THE VIEW
=============
View should already be familiar to you from the first two sections of this
book. However, since View is so important, we will briefly discuss it again.
View is the link between our creativity and the computer. The structure
strut View contains all the necessary View data.
Use InitView(&View) to intialise the View structure for all the individual
variables.
-----------------------------------------------------------------------------
9.3 The Foreman: The VIEWPORT
=============================
This is where you determine the size of the creativity area, the display
mode and the available colours. All of this data is sent to the ViewPort.
This data is later used to create the Copper list that the special Copper
coprocessor will use to construct your screen.
Before you can send data to the ViewPort you must first specify the
position on screen at which data should appear. The variables View.DxOffset
and View.DyOffset establish this position on the screen: One sets the
X-coordinate on the screen, while the other sets the Y-ccordinate. InitView
initialises these variables so that your View position matches the
positionin set in the Preferences (i.e. The corner angle in your preferences
screen which helps centre the screen).
If you enter a zro valu for ViewPort.DxOffset and ViewPort.DyOffset the
upper left corner of the ViewPort will be positioned at exactly the same
location as the View. Different values (larger than zero) will place your
ViewPort somewhere in the middle of the screen.
ViewPort.DWidth and ViewPort.DHeight set the size of the ViewPort. When
setting these variables you must remember which resultion you selected.
If you selected 640 pixels horizontal resolution (ViewPort.mode = hires),
you must specify a ViewPort.DWidth of 640 (instead of 320 normal mode).
This also applies to ViewPort.Mode = LACE (interlace mode), which must
have a ViewPort.DHeight of 400 instead of 200 (PAL 51 instead of 256).
PAGE 317
-----------------------------------------------------------------------------
A color map (or palette) is required for the color resolution. Since the
different color entries require memory, we must assign them.
ColorMap = (struct ColorMap *) GetColorMap
(Number_of_Colors)
sets up an entire structure called the ColorMap structure. This structure
stores the pointers to the color memory and the number of available colors
for the ViewPort.
After you initialise the ColorMap you must make it available to the
ViewPort. You can send a pointer for the color map to the ViewPort like
this:
ViewPort.ColorMap = ColorMap
or make a direct call like this:
ViewPort.ColorMap = (struct ColorMap *)GetColorMap(Number_of_colors)
It is important that you use the Cast ('...(struct ColorMap*)') to
prevent warnings (pointers do not point to same object).
You can determine the returned values of the function GetColorMap (and
all other functions) before the program sets them:
extern struct ColorMap *GetColorMap()
.....
main()
Now the compiler will no display the warning messages when you call
ViewPort.ColorMap = GetColorMap(Number_of_Colors).
To prevent the ViewPort structure from containing any random values that
would confuse the system, we also have an initialise command:
InitVPort(&ViewPort)
You should always use this command before writing any values to the
ViewPort or using the ViewPort.
Before we can make use of the BitMap we have to make a connection between
the ViewPort and the View:
View.ViewPort = &ViewPort
PAGE 318
-----------------------------------------------------------------------------
9.4 The Worker: the BIT-MAP
===========================
At some time and in some place our graphic must be entered into memory.
The bitmap controls both when and where this will occur. Then this
information is divided among different bitplanes. The number of bitplanes
sets how many colours are located in the ViewPort and displayed by the
BitMap. The more bitplanes there are the more colors. The actula number
of colours is calculated below:
Number_of_Colors = 2^Number_of_bitplanes
This calculation is based on the way the planes are graphically layered
on top of each other in the display (in memory they are actually stored
one after another).
Each pixel is represented by one bit in every bitplane. The combination
of all set an unset bits in a pixel in all the bitplanes determines the
number of the colour register (See chapter 14). The pixel is then
dispayed in that colour.
The AllocRaster function (a subset of the AllocMem function) allocates
memory (bytes) for the individual bitplanes. This function also makes
sure that the assigned memory area begins at an even word address. This
is necessary since the system can only access even word addresses.
You use BitMap.Planes[i] = AllocRaster(width,Height) to assign the
required memory for a bit-plane of the bitmap.
PAGE 319
-----------------------------------------------------------------------------
Width sets the width in pixels and height sets the height in rows. You must
make sure that the pointer BitMap.Planes[i] never equals zero. If this
happens, you will be unable to reserve enough memory. (The same thing can
happen when you use a value of zero with OpenLibrary or GetColorMap). Most
of the time this results in insufficient memory problems. When this
happens, try to have your program be the only one active (other than the
CLI or WorkBench) in your Amiga. Test the return values of the functions
and have them print an attention message if they receive a value of zero.
This will let all users know which memory areas cannot be used. For
example:
printf ("No more memort available for ColorMap/n");
exit(0);
Now back to the number of bitplanes. The system provides us with eight
BitMap.Planes[1...8] pointers. At the moment (Kickstart 1.2, Amiga 2000)
only a maximum of six of these can be used. Later, by expanding the system,
it may be possible to use all of them.
Six bitplanes can provide up to 2^6 = 64 displayed colors. There are
actually only 32 possible colours based on the pixel-bits (the Amiga
2000/1000/500 only has 32 colour registers).
We only require that five bitplanes have 32 colours. The sixth bitplane is
used with HAM and Halfbrite modes. (see chapter 17.)
There is still another limitation to discuss. You can only work with 4
bitplanes in hi-res mode or 640 pixel horizontal resolution. The reason for
this is that we have more data to display on the screen. The four
bitplanes use up the limited time the Amiga has available to display all
of this data.
Before we continue with the bitmaps we will show you how to initialise
the bitplanes (or memory).
Since we cannot guarantee that the memory is clear, we must clear it
ourselves (otherwise we many have a mess on the screen).
BltClear(BitMap.Planes[i],Number_of_Bytes,Flags) is used to clear
memory. To calculate the number of bytes to clear use the following:
Number_of_Bytes = Width*Height/8
The macro RASSIZE(Width,Height) calculates the same value (For the
parameter flags please see Appendix B. Normally flags are set equal to
zero).
PAGE 320
-----------------------------------------------------------------------------
The memory area information that you receive from AllocRaster is
distributed in an individual bitplane row like this:
The bytes '0 to width/8-1' represent the memory for the first row of the
bitplane. The bytes 'width/8 to 2*width/8-1' represent the second row and
this pattern continues to the last byte.
Now that we have closely studied the bitplanes, we will move to the
bitmap (the bitplane factory).
We set the size of the bitmap and the depth (number of bitplanes) in the
bitmap structure. The size of the BitMap and of the ViewPort can be
different.
The only thing you have to remember is that the bitmap must be within the
1024*1024 row/pixel limit. You initialise the bitmap with :
InitBitMap(&BitMap,Depth,Width,Height);
PAGE 321
-----------------------------------------------------------------------------
9.5 THE MESSANGER: RasInfo.
===========================
The BitMap and the ViewPort are still isolated from each other. We can
connect them by using the RasInfo structure. This structure isnt
established with an initialise procedure. You must program this structure,
element by element, by writing the necessary values into it.
The actual connetion between the ViewPort and the bitmap to the RasInfo
structure is made as follows:
ViewPort.RasInfo = &RasInfo
RasInfo.BitMap = &BitMap
The bitmap pointer of the RasInfo stucture (Raster information) is the
only common element. The only remaining variables to be initialised are the
RxOffset and the RyOffset. These specify the pixel coordinates for the
upper left corner of the ViewPorts and are normally initialised to zero.
The last RasInfo variable, the RasInfo.Next pointer, is used only for
special display modes (see chapter 17) and is normally set to zero.
PAGE 322
-----------------------------------------------------------------------------
9.6: The Laborer: THE RASTPORT.
===============================
We can compare the RastPort, which means the RastPort structure, to a heavy
laborer. We process almost all of the graphic output through the RastPort
because it contains the actual color of the pixels, the drawing mode (See
chapter 11) and much more (See appendix A).
InitRastPort(&RastPort) initialises the RastPort. This initialisation sets
up default values that can be changed later by using the proper commands.
After initialising the RastPort, you need to supply the address of the
BitMap where your graphic commands will become visible:
RastPort.BitMap = &BitMap
Now we have initialised all the required structures and linked them
together (The RastPort does not require a link to the ViewPOrt because
it only provides information for the graphic commands).
Until now, nothing has appeared on the screen. Finally, we can start
creating something that will be shown on the screen.
The reason why there hasnt been anything shown on the screen is because the
Copper list created by special programs and used by the Copper coprocessor
doesn't exist yet. The hardware registers are espcially affected by the
copper list which is required to control the graphic display. See appendix
C, register bltcon/1, bplcon0/1/2 etc...
The copper list is created by using MakeVPort(&View,&ViewPort) and
MrgCop(&View) which use the data initialised in View, ViewPort etc...
First we use MakeVPort to create an intermediate Copper list for every
ViewPort (There can be more than one ViewPort in a View but you must make
sure that a difference of at least on pixel row exists between the
ViewPorts when they overlap). The Copper itself is unable to do anything
with these intermediate lists. Once all the ViewPort Copperlists are
created, we use MrgCop to make a final copperlist that the Copper is able
to use. To make this list active, use LoadView(&View).
Finally, the copper works on the Copper list and you can see the results
on the screen. Before activating the copper list you should save the
existing Copper list in a variable (oldview= GfxBase->ActiView). The
GfxBase structure helps us do this because it always contains a pointer to
the actual View.
PAGE 323
-----------------------------------------------------------------------------
If, just one time, you forget to save your old view by calling
LoadView(oldview) before running your program, you will be lost in the
Amiga jungle. Since we start most programs from the CLI or Workbench, you
wont be able to return to the WorkBench screen where the CLI window is
located.
The following illustration shows how the View, ViewPort, RastPort, bitmap,
RasInfo etc.. are linked together:
__________
| View |
|----------| __________
| ViewPort |---->| ViewPort |
|__________| |----------|
| Next |
|----------| ____________
| ColorMap |---->| ColorMap |
__________ |----------| |------------| ___________
| RasInfo |<----| RasInfo | | Color Table|---->|Color0 |
|----------| |__________| |____________| |-----------|
| Next | |Color 1 |
|----------| ___________ ____________ |-----------|
| BitMap |---->| Bitmap |<---| RastPort | | ......... |
|__________| |-----------| |------------| |-----------|
| Planes[x] | | (TmpRas) | |Color 32 |
|___________| |------------| |___________|
| (Areainfo)|
/ | \ |------------|
/ | \ | (Gelsinfo)|
/ | \ |____________|
To the six bitplanes.
PAGE 324
-----------------------------------------------------------------------------
9.7 FINISH THE DAY.
===================
In our programs, "finish the day" means we still have more work to do.First
we have to return the reserved memory back to the system. Then we have to
close all opened libraries sing CloseLibrary(base pointer). The base pointer
for the graphics library must be GfxBase.
When you are returning memory, you must remember that the Copper list you
created with MakeVPort and MrgCop uses memory also.
To release the ViewPort intermediate lists, use FreeVPortCopLists(&View
Port). Then use FreeCprList(View LOFCprList) and FreeCprList (View
SHFCprList) to release the actual Copper list (See Appendix A for View/
LOF/SHFCprList).
FreeRaster (Bitmap Planes [i]), Width, Height) is used to release the
memory for every bit-plane used).
To release the reserved memory for the color map, use FreeColorMap
(&ColorMap) and FreeColorMap(ViewPort.ColorMap).
PAGE 325
-----------------------------------------------------------------------------
CHAPTER 10 : LINES AND PIXELS.
==============================
Now we can begin our discussion of our first graphic commands. The smallest
element in all computer graphics is the pixel.
-----------------------------------------------
10.1 PIXELS SET WITH WRITEPIXEL.
================================
You can set a single pixel in the bitmap with the WritePixel(&RastPort, x,y)
function. The RastPort, which is one of the required parameters, contains
the colour used to write the pixel to the bitmap.
The X and Y parameters specify the two-dimensional coordinate for the
pixel. Please remember that the coordinates begin from the upper-left
corner. The Y coordinate value increases as you go move towards the
bottom of the screen. The X coordinate increases as you move towards the
right side of the screen.
(0,0) (320/640,0)
___________________________________________________>
| x
|
|
|
|
|
|
|
|
|
|
|
|
| (0,200/400) y
\/
The following program opens a View, ViewPort etc. It uses WritePixel
to create a string of pixels. We set each pixel in the string to a
different colour. After the drawing is complete, we rotate the contents of
the colour registers with the ColorMap function (see Chapter 14). This
creates the familiar color cyclin effect used by Deluxe Paint.
PAGE 327
-----------------------------------------------------------------------------
/*********************************************/
/* */
/* strings.c */
/* */
/* This program uses WritePixel() to create */
/* a string that uses colour cycling */
/* animation. */
/* */
/* Compile With : Lattice V5. */
/*********************************************/
#include "exec/types.h"
#include "exec/memory.h"
#include "exec/devices.h"
#include "graphics/gfx.h"
#include "graphics/gfxbase.h"
#include "graphics/gfxmacros.h"
#include "graphics/text.h"
#include "graphics/view.h"
#include "graphics/clip.h"
#include "graphics/copper.h"
#include "graphics/gels.h"
#include "graphics/regions.h"
#include "hardware/blit.h"
#include "devices/keymap.h"
#define Width 320
#define Height 200
#define Depth 4
#define MODES 0 /* REsolution mode */
struct GfxBase *GfxBase;
struct View View; /* structures for our display */
struct ViewPort ViewPort;
struct RasInfo RasInfo;
struct BitMap BitMap;
struct RastPort RastPort;
struct View *oldView;
int i,x,y,factor;
UWORD color = 0; /* Colour counter */
USHORT Colors[16] = {
0x000, 0x00f, 0x0f0, 0xf00,
0x0ff, 0xff0, 0xf0f, 0xc34,
0x646, 0x782, 0xd23, 0x5a9,
0x560, 0xacf, 0xedf, 0xa09
};
/* Initial color table */
char *LeftMouse = (char *)0xbfe001;
VOID Color(); /* Forward Declaration */
VOID Color_Cycle();
PAGE 328
---------------------------------------------------------------------------
/*******************************************/
/* Here We Go ! */
/*******************************************/
main()
{
if((GfxBase = (struct GfxBase *)
OpenLibrary("graphics.library",0))==NULL)
{
printf("No graphics library !!!!");
exit(0);
}
oldView = GfxBase->ActiView; /* Save old View */
InitView(&View); /* Initialise View*/
InitVPort(&ViewPort); /* Initialise ViewPort */
View.ViewPort = &ViewPort; /* Link view and viewport */
View.Modes = MODES; /* Set viewmodes */
ViewPort.DWidth = Width; /* Set viewport parameters */
ViewPort.DHeight= Height;
ViewPort.RasInfo= &RasInfo;
ViewPort.Modes = MODES;
ViewPort.ColorMap = (struct ColorMap *)GetColorMap(16);
if(ViewPort.ColorMap == 0) goto cleanup1;
/* Set colour map */
RasInfo.Next =NULL; /* Link ViewPort-Bitmap */
RasInfo.RxOffset = 0; /* thru RasInfo */
RasInfo.RyOffset = 0;
RasInfo.BitMap = &BitMap;
InitBitMap( &BitMap, Depth, Width, Height);
/* Initialise BitMap */
for (i=0; i<depth; i++)
{
BitMap.Planes[i] = (PLANEPTR)
AllocRaster(Width,Height);
if (BitMap.Planes[i] == 0)
{
printf("No memory for bitplanes \n");
goto cleanup1;
}
else
BltClear(BitMap.Planes[i],
RASSIZE(Width, Height),0);
/* Erase Bitplanes *
}
PAGE 329
-----------------------------------------------------------------------------
InitRastPort(&RastPort) ; /* Initialise Rastport */
RastPort.BitMap = &BitMap; /* Link RastPort BitMap
LoadRGB4(&ViewPort,&Colors[0],16);
/* Write colors to ViewPort */
/* ColorMap. Follow this with */
/* MakeVPort(), MrCop() and */
/* LoadView() */
/* (Intuition:RemakeDisplay())*/
MakeVPort(&View, &ViewPort);
MrgCop(&View);
LoadView(&View);
color = 1; /* Color Animation */
x=0;
y=0;
factor = 1;
while (y < height-1)
{
SetAPen(&RastPort,color);
WritePixel(&RastPort,x,y);
x+= (factor);
if ((x>width-1) || (x==0))
{
for(i=0; i<7; i++)
{
Color();
SetAPen(&RastPort,color);
WritePixel(&RastPort,x,y+1);
}
factor *= -1;
y +=6;
}
Color();
}
while ((*LeftMouse & 0x40) == 0x40)
{
Color_Cycle();
}
FreeColorMap(ViewPort.ColorMap); /* Set */
FreeVPortCopLists(&ViewPort); /* all */
FreeCprList(View.LOFCprList); /* free */
FreeCprList(View.SHFCprList);
cleanup1: for(i=0; i<depth; i++)
{
if (BitMap.Planes[i] != NULL)
FreeRaster (BitMap.Planes[i],Width,Height);
}
LoadView(oldView);
CloseLibrary(GfxBase);
return(0);
}
PAGE 330
----------------------------------------------------------------------------
/************************************************/
/* */
/* This routine takes care of incrementing the */
/* color counter while drawing. */
/*----------------------------------------------*/
/* Entry Parameters : None */
/*----------------------------------------------*/
/* Returned Values : None */
/*----------------------------------------------*/
VOID Color()
{
color++;
color&=15;
if (color==0) color = 1;
}
/************************************************/
/* */
/* Thsi routine takes care of rotating the */
/* colours of the ViewPort ColorMap. */
/*----------------------------------------------*/
/* Entry Parameters : None */
/*----------------------------------------------*/
/* Returned Values : None */
/************************************************/
VOID Color_Cycle()
{
UWORD i,help;
static UWORD Cols[16];
Cols[0] = Colors[0]; /* Background Color Remains */
help = GetRGB4 (ViewPort.ColorMap,15);
for (i=2; i<16; i++)
Cols[i] = GetRGB4(ViewPort.ColorMap,i-1);
Cols[1] = help;
/* Shuffle */
LoadRGB4(&ViewPort,&Cols[0],16);
MakeVPort (&View,&ViewPort); /* Important !!! */
MrgCop(&View);
LoadView(&View);
}
PAGE 331
--------------------------------------------------------------------------
10.2 DRAWING LINES WITH MOVE AND DRAW.
Naturally, the Amiga can also draw lines. What is remrakable about this
capability is that the 68000CPU is hardly used for this function.
Instead, the blitter, (one of the Amigas special coprocessors) executes
this function independantly.
First we have to set the starting coordinates. Drawing lines in C
language differes from drawing lines in BASIC because C requires two
functions instead of only one.
The MOVE function sets the actual graphic position (graphic cursor) to the
specified coordinates:
Move (&RastPort,x,y)
At the same time, there coordinates are stored in the reference RastPort
(RastPort.cp_x and RastPort.cp_y).
The Draw(&RastPort,x,y) function draws a line to the specified coordinates.
These coodrinates automatically become the new graphic cursor positions.
The draw (&RastPort,x,y) function draws a line to the specified coordinates.
The next Draw function (without any moves in between) draws from the last
pixel that was drawn to the pixel coordinates specified by the new Draw
function.
The following program demonstrates the powerful Amiga capability:
/*******************************************************/
/* Quix.c */
/* */
/* This program demonstrates the hidden speed of the */
/* Amiga, especially when drawing lines. */
/* */
/* Compiled with LAttice V5 */
/* */
/* (c) Buno Jenrich */
/*******************************************************/
#include "exec/types.h"
#include "graphics/gfx.h"
#include "graphics/copper.h"
#include "graphics/view.h"
#include "graphics/gels.h"
#include "graphics/regions.h"
#include "graphics/clip.h"
#include "graphics/text.h"
#include "graphics/gfxbase.h"
#include "graphics/rastport.h"
PAGE 332
----------------------------------------------------------------------------
#include "hardware/blit.i"
#define DEPTH 2 /* 2 bitplanes */
#define WIDTH 320 /* 320 * 200 pixels. */
#define HEIGHT 200
#define MAX_X WIDTH-2 /* Do not exceed borders */
#define MAX_Y HEIGHT-2 /* Left, Right, Bottom */
#define MIN_X 1
#define MIN_Y 10 /* Do not write over text */
#define NOT_ENOUGH_MEMORY -1000
#define MAX_LINES 30 /* Maximium 30 lines
struct View View; /* Our own structures */
struct ViewPort ViewPort;
struct RasInfo RasInfo; struct ColorMap *ColorMap
struct BitMap BitMap;
struct RastPort RastPort;
SHORT i,j,length;
struct GfxBase *GfxBase;
struct View *oldView; /* here we save the old view */
USHORT colortable[] = {0x000, 0xf00, 0x00f, 0x0f0);
/* our own colour palette */
char *QuixString = "Quix - Line *** (C) BHJ";
char *LeftMouse; /* For CIA address */
VOID draw(); /* Forward delcaration */
VOID FreeMemory();
/**************************************************/
/* Here we go ! */
/**************************************************/
main()
{
if((GfxBase = (struct GfxBase *)
OpenLibrary("graphics.library",0))==NULL)
exit(1);
oldview = GfxBase->ActiView; /* Save old View */
InitView(&View); /* initialise view and viewport */
InitVPort(&ViewPort);
View.ViewPort = &ViewPort; /* link them together */
InitBitMap(&BitMap, DEPTH, WIDTH, HEIGHT);
/* initialise bitmap */
InitRastPort(&RastPort); /* RastPort */
PAGE 333
---------------------------------------------------------------------------
RastPort.BitMap = &BitMap; /* RastPort ->BitMap
RasInfo.BitMap = &BitMap; /* RasInfo for BitMap */
RasInfo.RxOffset = 0;
RasInfo.RyOffset = 0;
RasInfo.Next = NULL;
ViewPort.RasInfo = &RasInfo; /* ViewPort -> RasInfo */
ViewPort.DWidth = WIDTH; /* How big is ViewPort ? */
ViewPort.DHeight = HEIGHT;
ViewPort.ColorMap = (struct ColorMap *)GetColorMap(4);
/* ViewPort's ColorMap */
LoadRGB4 (&ViewPort,&colortable[0],4);
/* Load ColorMap with Colors. */
for(i=0; i<DEPTH; i++);
{
if (BitMap.Planes[i] = (PLANPTR)
AllocRaster(WIDTH, HEIGHT)) == NULL)
Exit(NOT_ENOUGH_MEMORY);
/* Reserve memory for BitPlanes */
BltClear((UBYTE *)BitMap.Planes[i],
RASSIZE(WIDTH,HEIGHT),0);
/* Erase BitPlane memory */
}
MakeVPort(&View,&ViewPort); /* Copper list for ViewPort */
MrgCop(&View); /* "size" CopperList: for view */
LoadView(&View); /* switch new display on */
LeftMouse = (char *)Oxbfe001;
/* CIA address for I/O ports */
/* are used for Left */
/* Mouse Button */
draw(); /* Your routine */
LoadView(oldview); /* Workbench display on */
FreeMemory(); /* Return everything */
return(0);
}
/**********************************************************/
/* */
/* This function takes care of drawing the lines and */
/* reading the left mouse button. */
/*--------------------------------------------------------*/
/* Entry parameters : None */
/*--------------------------------------------------------*/
/* Returned values : None */
/*--------------------------------------------------------*/
PAGE 334
---------------------------------------------------------------------------
VOID draw()
{
static SHORT i,
new = 0,
old = 0,
full = FALSE,
min, temp,
delay =1999,
direction = 1,
k = 0;
struct { /* coordinates for one line */
int x1[MAX_LINES],
x2[MAX_LINES],
y1[MAX_LINES],
y2[MAX_LINES];
} line;
static int veloc[4] = {-4,5,3,-7};
/* what is added to the coordinate ? */
static int start[4] = {110,25,160,74};
/* Where is the first line drawn ? */
static int max[4] = {MAX_X, MAX_Y, MAX_X, MAX_Y};
/* Allowed maximum value of coordinates */
SetDrMd(&RastPort,JAM1);
SetAPen(&RastPort,3); /* Drawing Colors = Green */
Length = WIDTH/2-TextLength(&RastPort, QuixString,
strlen(QuixString))/2;
/* calculate central position */
Move(&RastPort,Length,RastPort,TxBaseline);
/* position graphic cursor */
Text(&RastPort, QuixString, strlen(QuixString));
/* display text */
SetAPen(&RastPort,1); /* drawing colour = red */
Move(&RastPort,0,9); /* draw frame */
Draw(&RastPort,WIDTH-1,9);
Draw(&RastPort,WIDTH-1, HEIGHT-1);
Draw(&RastPort,0,HEIGHT-1);
Draw(&RastPort,0,9);
while ((*LeftMouse & 0x40) == 0x40) /* Mouse button ? */
{
for(i=0; i<4; i++) /* calc new line coords */
{
temp = start[i] + veloc[i];
/* Exit value + "speed" */
if(temp>=max[i])
/* exceeds upper limit ? */
{
PAGE 335
---------------------------------------------------------------------------
temp=max[i]*2 - start[i] - veloc[i];
veloc[i] = -veloc[i]
}
if(i==1 | i==3) min = MIN_Y;
/* Check Y coords */
else min = MIN_X;
if (temp<min)
{
/* exceeds lower border ?*/
if (tmp<0) temp = -temp;
else temp = min;
veloc[i] = -veloc[i];
}
start[i] = temp;
}
if (full == TRUE)
{
/* Erase last line */
SetAPen(&RastPort,0);
Move(&RastPort,line.x1[old], line.x2[old]);
Draw(&RastPort,line.x2[old], line.y2[old]);
old++;
old% = MAX_LINES;
}
line.x1[new] = start[0]; /* set new line */
line.y1[new] = start[1]; /* coordinates */
line.x2[new] = start[2];
line.y2[new] = start[3];
if(new == MAX_LINES-1) full = TRUE;
/* MAX_LINES drawn ? */
SetAPen(&RastPort,2); /* Drawing colour = blue */
Move(&RastPort,line.x1[new],line.y1[new]);
Draw(&RastPort,line.x2[new],line.y2[new]);
/* Draw new line */
new++;
new%= MAX_LINES;
if(delay != 0)
{
for(i=0; i<delay; i++);
/* wait a bit *
delay += direction;
PAGE 336
--------------------------------------------------------------------------
if (delay = 2000) direction = -1;
}
else
{
k++;
if(k== 1000) /* 1000 times 'full power' */
{
direction = 1;
delay += direction;
k = ;
}
}
}
}
}
/*****************************************************/
/* This function returns allocated memory for bitmaps*/
/* copper lists etc... */
/*---------------------------------------------------*/
/* Entry parameters : None */
/*---------------------------------------------------*/
/* Returned values : None */
/*---------------------------------------------------*/
VOID FreeMemory()
{
for(i=0; i<DEPTH; i++)
FreeRaster(BitMap.Planes[i], WIDTH, HEIGHT);
/* Return bitplane memory */
FreeColorMap(ViewPort.ColorMap);
/* Release ColorMap memory that was */
/* reserved with ColorMap() */
FreeVPortCopLists(&ViewPort);
/* Free memory for */
/* ViewPort CopperList */
/* from MakeVPort() */
FreeCprList(View.LOFCprList);
FreeCprList(View.SHFCprList);
/* View Coppelist */
CloseLibrary(GfxBase);
/* Close Library */
}
We have two additional tips for you. It is possible to exit most of our
programs by using a mouse button. This is achieved by constantly checking
the hardware registers. You would probably find this quite useful in your
own programs.
If you dont like the line pattern we used, you can create a new pattern
with SetDrPt(&RastPort, Pattern). The 16 bit word pattern contains the
actual pixel pattern for the line. (we set Pattern = 0xffff which draws
a full line).
PAGE 337
----------------------------------------------------------------------------
11. COLOR: DRAWING PENS.
After the two previous programs were up and running, you probably
wondered how we calculated the colors for a pixel or a line.
To explain this we must look at the bitplanes. Either we or the individual
bits of bit planes determine a pixels colour.
For example, when the pixel bit of bitplanes one and three are set but
bitplane two is unset, we use the following formula: (1*2^0 + 0*2^1 +
1*2^2 = 5. This tells us that the colour comes from colour register five.
You could also use normal binary arithmetic.
To set a specific colour for a pixel, the opposite action is performed.
You specify the colour register to use and the graphic functions set the
individual pixel bits for you.
Remember the Amiga has two colour pens, the APen and the BPen. But before
we continue with their functions, we will quickly discuss DrawModes.
PAGE 339
--------------------------------------------------------------------------
11.1 THE DRAWMODES.
The drawmodes determine the relationship betweenthe existing pixel bits
and the pixel bit written to their locations in the bitplanes. The
available bit manipulation operations are OR, AND, NOT and EXOR.
In the normal mode (SetDrMd (&RastPort, JAM1)) the written bits are
simply ORed with the existing bitplane bits.
In JAM2 mode (SetDrMd (&RastPort, JAM2)) the written bits are added using
AND. This has the same effect as JAM1 when usig pixels and lines. However,
when using text you can see the difference immediately. A drawings bit
pattern is stored in memory as set and unset pixels. The blitter (BLock
Imae Transferrer), which is responsible for drawing output, also copies the
unset bits of a drawing to the bitplane. When JAM2 mode is on, the
background is overwritten with the unset bits.
The COMPLEMENT mode performs an EXOR operation between the existing bits
and the bits being written. The EXOR logic table looks like this:
Bitplane Operation:^ Pixel Result
---------------------------------------------------
0 0 = 0
0 1 = 1
1 0 = 1
1 1 = 0
PAGE 340
---------------------------------------------------------------------------
With the last mode, INVERSVID, the written bits are reversed using the NOT
operator. A set bit becomes unset and an unset bit is set.
When using the NOT and EXOR operations you should remember that they take
place internally. You only see the results when you use INVERSVID or
COMPLEMENT together with JAM1 or JAM2 (for example: SetDrMd (&RastPort,
INVERSVID | JAM2). The bit pattern is written normally, as with JAM1 and
JAM2, using OR or AND. The result is usually a chaotic coloured line or
text with coloured spots.
----------------------------------------
11.2 THE FOREGROUND PEN.
Now that we have explained the drawing modes, we can continue with the
drawing pens. To set the pixel colour in JAM1 mode, use SetAPen (&RastPort,
Number_of_Color_Register). When working with pixels and lines, JAM1 and
JAM2 mode have the same effect.
----------------------------------------
11.3 THE BACKGROUND PEN.
When outputting text you can use JAM2 mode and the BPen (background pen)
to display highlighted text. To do this, simply select a color for the
unset pixels on the screen using:
SetBPen(&RastPort, Number_of_Color_Register).
You should be aware that the function of the APen and BPen affect the bit
patterns in the bitplanes. They determine the bit by bit pattern that is
always used when you write data to a specific location.
PAGE 341
---------------------------------------------------------------------------
12. INTUITION AND GRAPHICS.
---------------------------
Now that you have seen how to draw pixels and lines, change drawings modes
and select drawing pens, we will show you a rather easy method to set up
View, ViewPort etc..
This method involves the user interface Intuition, which enables you to
easily access the RastPorts.
The first and most important step is opening the Intuition.library. You
do this in the same way as you opened the graphic.library. But instead
of using the string name "graphics.library",you use "intuition.library".
The function looks like this:
(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.
library",0)).
PAGE 343
----------------------------------------------------------------------------
12.1 THE INDIVIDUAL SCREEN.
Intuition creates screens that are completely initialised ViewPorts with a
ColorMap, a BitMap, a RastPort etc..
Inuition also creates a View for us. However, the most important feature
of Intuition is that it takes control of almost everything so we dont
have to do the work ourselves.
Basically, opening screens is reduced to two actions:
1.) Initialising a NewScreen.
2.) the OpenScreen call.
For details on the NewScreen structure please check the Appendices. We will
only briefly explain the Screen = (struct Screen *)OpenScreen(&NewScreen).
This call creates a displayed, fully functional screen with a screen
structure that we can access. (NOTE: Screen and NewScreen are not the
same!). &Screen->RastPort is used with most of the graphic commands to
access the RastPort.
The best example of an Intuition screen is the Workbench screen where all
of the Workbench activities are performed.
Remember that OpenScreen uses up a lot of memory. If the pointer Screen,
For the initialised screen structure is equal to zero, then there
isnt any available memory. When this happens, it is best to have the
program display a short error message and exit to the CLI or Workbench.
PAGE 344
---------------------------------------------------------------------------
12.2 THE WINDOW.
Most of the time, windows are used for graphic output. One advantage of
windows is that a drawn line cannot pass over the window border because it
will be cut off before this can happen. This is not true for screens and
you own ViewPorts. Under certain conditions, the excess portion of the line
will be written to memory, which causes loss of data.
Opening windows requires two actions:
1.) Initialising a NewWindow structure and
2.) Calling Window = (struct Window *) OpenWindow(&NewWindow)
You can access the RastPort of a window by using &Window->RastPort.
----------------------------------------
12.3 EXITING INTUITION.
We must return the memory used by Intuition for the same reason we had to
return bitplane memory. You must also release the memory that was used
by the screens and windows.
Close a screen using CloseScreen (Screen). The window is closed in the
same way with CloseWindow(Window). It is very important that you close
the windows before closing the screens otherwise you will receive
Guru Meditations.
Obviously, you must also close the graphic.library and Intuition.Library:
Close.Library(IntuitionBase)
PAGE 345
---------------------------------------------------------------------------
13. FILLING AREAS IN C.
Now that we know how Intuition can help us, we can being working on
"areas".
13.1 A Flood Function.
We can fill connected areas using the "Flood" function. For a pictorial
image of how flood works, compare Flood to pouring paint into a bathtub.
If you do notpour more that the tub can hold, the paint will not flow past
the edge. However, the effect would be completey different if the tub had
a crack in it causing the paint to spill out of the tub.
We determine the edges for a Flood function by drawing a frame around the
area we want filled. This frame does not have to be square or have a
specific shape. To perform the fill you must specify coordinates that
are inside the area to be filled:
Flood(&RastPort,Mode,x,y)
With this type of fill, you must use a mode parameter equal to zero. This
means that you fill all pixels, using the curent drawing colour in the
current drawing mode, with the current fill pattern until you reach a
border.
As explained above, you set the border by drawing a completely closed
frame in a specific colour. To set the border colour use SetOPen(&RastPort,
Color_of_Frame). (OPen = AOLPen = Area Outline Pen = Color of the surface).
When the mode parameter us equal to one a slightly different fill method
is being used. The colour of the frame is no longer important. You only
fill the connected area that have the same colour as the pixel at the
selected X/Y coordinate.
You should be careful when using the flood function because it uses up a
large amount of memory.
Due to a recursive algorithm you have to prepare a temporary raster (a
temporarily used bitplane). This bitplane must be at least as large as
the object you want to fill.
PAGE 347
---------------------------------------------------------------------------
It is much safer to create a complete additional bitplane (AllocRaster).
Then you can make this memory available to the TmpRas structure and pass
this information to the RastPort.
InitTmpRas (&TmpRas, &Memoryaddress, MemorySize)
RastPort.TmpRas = &TmpRas
or
RastPort.TmpRas = InitTmpRas(&TmpRas,&Memoryaddress,MemorySize)
Remember that you have to return the memory for the additional bitplane
when you are finished.
PAGE 348
---------------------------------------------------------------------------
13.2 FILLING RECTANGLES ?
Besides filling connected areas, the Amiga can also fill simpler areas.
This is an important reason for the blitter.
You can fill rectangles with the RectFill(&RastPort,x1,y1,x2,y2) command.
The 1,y1 coordinates set the upper left corner and the x2,y2 coordinates
set the lower right corner for the filled rectangle. Please make sure that
the upper left coordinates are really to the left and above the coordinates
for the lower right corner. You will get a Guru and a system lockup if
your coordinatesare not correctly specified.
This command automatically draws the frame for the filled rectangle. you
set the color for the frame with OPen(&RastPort,Color_of_Frame).
If you want to fill the rectangle without a frame, you use the macro
BNDRYOFF (&RastPort) whih stands for BOUNDARY OFF. This macro simply clears
the AREAOUTLINE bit in the flag variable of the RastPort. To fill with the
visibile frame again, restore the areaoutline bit, use RastPort.Flags
|= AREAOUTLINE. Changing the colour of the OPen (with SetOPPen()) auto-
matically sets this bit.
The following program uses this method of area filling without requiring
a TmpRas (additional bitplane):
/*****************************************************/
/* Hanoi.c */
/* This program shows you firsthand the Tower of */
/* Hanoi and the use of the RectFill() command. */
/* */
/* Compiled with : Lattice V5. */
/*****************************************************/
#include "exec/types.h"
#include "exec/devices.h"
#include "intuition/intuition.h"
#include "intuition/intuitionbase.h"
#include "graphics/gfx.h"
#include "graphics/gfxbase.h"
#include "graphics/gfxmacros.h"
#include "graphics/copper.h"
#include "graphics/gels.h"
PAGE 349
---------------------------------------------------------------------------
#include "graphics/regions.h"
#include "devices/keymap.h"
#include "hardware/blit.h"
#define GFX (struct GfxBase *)
#define INT (struct IntuitionBase *)
#define SCR (struct Screen *)
#define MAXHEIGHT 30 /* maximum 30 disks.*/
#define WIDTH 640
#define HEIGHT 200
#define RPort &Screen->RastPort /* Our rastport */
struct NewScreen Newscreen =
{
0,0,WIDTH,HEIGHT,2,
1,0,HIRES,CUSTOMSCREEN,
NULL,NULL,NULL
};
char *String = "Tower of Hanoi with RectFill()";
struct Screen *Screen;
struct GfxBase *GfxBase;
struct IntuitionBase *IntuitionBase;
UWORD pattern[4] = {0x9248,0x2492,0x4924,0x9248};
long pos[3]; /* For pin position */
long pole[3][MAXHEIGHT], /* for which pin at */
/* which Position, */
/* which disk ? */
height[3] = {0,0,0}; /* how many disks on */
/* the pins ?? */
long width;
long disks; /* How many disks on the starting tower */
long high, offset;
char *LeftMouse = (char *)0xbfe001;
VOID turm(); /* Forward Declaration */
VOID init();
VOID build_up();
VOID draw();
/*******************************************/
/* Here we go ! */
/*******************************************/
main(argc,argv) /* Get argument */
PAGE 350
---------------------------------------------------------------------------
int argc;
char *argv[];
{
long i,j, length;
if (!(Argc==2))
{
printf(" Tower Height : ");
scanf ("%ld", &disks);
}
else
sscanf(argv[1],"%ld",&disks);
if (disks >= MAXHEIGHT)
{
printf(" Too many disks !!!\n");
exit(1);
}
/* Calculate Pin (x-) position for display */
pos[0] = WIDTH/6+5; /* Left */
pos[1] = WIDTH/2; /* middle */
pos[2] = (WIDTH/6)*5-5; /* Right */
width = (WIDTH/6)/disks-2; /* Width difference */
init();
SetRGB4(&Screen->ViewPort,0,0,0,0); /* Set colours */
SetRGB4(&Screen->ViewPort,1,15,15,0);
SetRGB4(&Screen->ViewPort,2,0,15,15);
SetRGB4(&Screen->ViewPort,3,15,0,15);
/* Initialise Pins */
for (i=0; i<MAXHEIGHT; i++)
for(j=0; j<3 ; j++)
pole[j][i] = 0;
/* Erase Pin */
for (i=0; i<disks; i++)
pole[0][i] = disks - 1;
/* Smallest disk to top */
height[0] = disks; /* 1 pin has n disks */
SetAfPt(RPort,pattern,2); /* Set fill pattern */
SetOPen(RPort,2); /* Framing pen */
high = (HEIGHT*3/8)/disks;
/* How high is one disk ? */
offset = HEIGHT*3/8;
build_up(); /* Build first display */
PAGE 351
---------------------------------------------------------------------------
SetAPen(RPort,3); /* String output */
Length = TextLength(RPort, String, strlen(String))/2;
Move(RPort,WIDTH/2-Length,Screen->RastPort,TxBaseline);
Text(RPort,String,strlen(String));
/* Start recursions */
turm (disks,1,2,3);
while ((*LeftMouse & 0x40) == 0x40);
/* Wait a little bit */
CloseScreen(Screen);
CloseLibrary(GfxBase);
CloseLibrary(IntuitionBase);
return(0);
}
/******************************************************/
/* This recursive procedure moves the disks from pin */
/* one to pin three. */
/*----------------------------------------------------*/
/* Entry Parameters: */
/* n :: = Number of disks. */
/* l :: = Number of left pin. */
/* m :: = "" "" middle pin. */
/* r :: = "" "" right pin. */
/*----------------------------------------------------*/
/* Returned values : none. */
/******************************************************/
VOID turm(n,l,m,r)
long n,l,m,r;
{
if (n==1)
{
pole[r-1][height[r-1]] =
pole[l-1][height[1-1]-1];
height[r-1]++;
draw(r-1, l-1);
pole[l-1][height[l-1]-1] = 0;
height[l-1]--;
}
else
{
turm(n-1,l,r,m);
pole[r-1][height[r-1]] =
pole[-1][height[l-1]-1];
height[r-1]++;
draw(r-1,l-1);
pole[l-1][height[l-1]-1] = 0;
height[l-1]--;
PAGE 352
---------------------------------------------------------------------------
turm(n-1,m,l,r);
}
}
/*****************************************************/
/* This procedure opens the required libraries and */
/* a screen. */
/*---------------------------------------------------*/
/* Entry parameters : None, */
/*---------------------------------------------------*/
/* Returned values : None. */
/*****************************************************/
VOID init()
{
GfxBase = GFX OpenLibrary("graphics.library",0);
IntuitionBase = INT OpenLibrary("intuition.library",0);
Screen = SCR OpenScreen(&NewScreen);
}
/**************************************************/
/* This procedure builds the three towers to start*/
/*------------------------------------------------*/
/* Entry parameters : none */
/*------------------------------------------------*/
/* Returned values : none */
/**************************************************/
VOID Build_up()
{
long i,j;
WaitTOF(); /* Synchronised output with beam */
SetRast(RPort,0);
SetAPen(RPort,1);
for (j=0; j<3; j++)
for(i=1; i<disks; i++)
RectFill(RPort, pos[j]-(pole[j][disks-i]*width),
((i-1)*high)+offset, pos[j]+(pole[j][disks-i]
*width), (i*high)+offset);
}
/*********************************************************/
/* This procedure erases and sets the top disk for */
/* two pins. */
/*-------------------------------------------------------*/
/* Entry Parameters: */
/* new := Pin, where new disk is placed. */
/* old := Pin, where disk is removed from. */
/*-------------------------------------------------------*/
/* Returned values: none. */
/*********************************************************/
PAGE 353
---------------------------------------------------------------------------
VOID draw(new,old)
long new, old;
{
SetAPen(RPort,0);
SetOPen(RPort,0);
if(height[old]-1 == 0) /* Last pin ? */
RectFill(RPort, pos[old][height[old]-1]*width,(disks-height
[old])*high+offset, pos[old]+pole[old][height[old]-1]
*width, (disks-height[old]+1)*high+offset);
else
RectFill(RPort, pos[old-pole[old][height[old]-1]*width,
(disks-height[old])*high+offset, pos[old]+pole[old]
[height[old]-1]*width, (disks-height[old]+1)*high
+offset-1);
SetAPen(RPort,1); /* Write new disk */
SetOPen(RPort,2);
RectFill(RPort,pos[old],(disks-height[old])*high+offset,
pos[old], (disks-height[old]+1)*high+offset);
RectFill(RPort, pos[new]-pole[new][height[new]-1]*width,
(disks-height[new])*high+offset, pos[new]+pole[new]
[height[new]-1]*width, (disks-height[new]+1)*high+
offset);
Delay(Screen->MouseX/10);
/* Pause depending upon the mouse position. */
}
If you look very closely, you can see that the rectangles drawn by the
program are not completely filled. There are many small holes that allow
the background to show through.
We were able to do this becuase we created our own fill pattern as we
mentioned in the Flood function section.
In addition, there are two types of fill patterns, single coloured and
multicoloured. Both of these fill patterns requie a height that is set in
powers of two (1,2,4,8 ... rows) and a width of 16 pixels (bits). WE
store the fill pattern in memory, for example in a word array.
PAGE 354
---------------------------------------------------------------------------
Single coloured patterns require only a single bitplane. The colours of the
APen (BPen) and drawing mode also have an effect on the area's appearance.
With multi-colored planes you must specify a bit pattern for every plane.
By using the current drawing mode, you can write these patterns directly
into the bitplanes. The only way we can affect them there is by changing
the drawing mode.
To use these patterns with the graphics functions, we must get help from
the SetAfPt(&RastPort, &BitPattern, Height) function (Set Area Fill
Pattern).
We specify the height as an exponent of the power of two. So a pattern that
is eight rows high then has a height of 3 (3^2 = 8).
However, this only applies to single coloured patterns. When using multi
coloured patterns, we set the exponent to a negative value, -3 instead
of 3.
PAGE 355
---------------------------------------------------------------------------
13.3 POLYGON FILLING: AREA FILL MAKES IT POSSIBLE.
The AREA... functions provide another method for filling in areas. These
commands enable you to draw filled polygons with a frame drawn in the
OPen colour.
Before you are able to use these commands, some preparatory work is
required.
The most important setup is, as in the FLOOD function, the TmpRas structure.
You must initialise this in the RastPort where you want to draw your
polygons.
You must also initialise the AreaInfo structure that will contain the
individual base points of the polygon. To do this, use the function:
InitArea(&AreaInfo, &CoordsBuffer, NumCoords)
InitArea ensures that the AreaInfo structure for the buffer and the address
of this buffer are established. These will later contain the polygon
coordinates. You also have to set the maximum number of coordinates for
this buffer (NumCoords). Your buffer must contain (NumCoords+1)*5 bytes.
The easiest way to do this is to assign the memory by using a char array
(char buffer[ (NumCoords+1)*5]).
After you pass the initialised AreaInfo structure to the RastPort
(RastPort.AreaInfo = &AreaInfo) you can set up your polygon. You always
set the first coordinate with AreaMove(&RastPort, x, y). AreaMove signals
that you are creating a new polygon.
Now you can set the rest of the polygon base points using
AreaDraw(&RastPort, x, y). In this case x and y also specify the coordinates
of the pixel within the RastPort.
You set the last pixel of a polygon with AreaEnd (&RastPort, x, y). This
also takes care of drawing the polygon (a submode of the Flood function)
and filling it.
You can also use the BNDRYOFF (&RastPort) macro to turn off the boundary
lines for the polygon.
PAGE 356
---------------------------------------------------------------------------
To complete this chapter, we have included a special program. This program
allows you to outline, with the mouse, objects that we can then rotate
three dimensionally.
/********************************************************/
/* Rotateit.c */
/* */
/* This program creates three dimensional rotation */
/* objects that we build with Area()... and display. */
/* Compiled with Lattice v3.1 */
/* Will require major changes for Lattice V5 */
/* and Aztec 3.6a */
/********************************************************/
#include "exec/types.h"
#include "exec/memory.h"
#include "exec/devices.h"
#include "devices/printer.h"
#include "devices/keymap.h"
#include "graphics/gfx.h"
#include "graphics/gfxmacros.h"
#include "graphics/gfxbase.h"
#include "graphics/rastport.h"
#include "graphics/text.h"
#include "graphics/regions.h"
#include "graphics/gels.h"
#include "graphics/copper.h"
#include "hardware/blit.h"
#include "intuition/intuition.h"
#define WIDTH 640
#define HEIGHT 400 /* Size of Screen */
PAGE 357
--------------------------------------------------------------------------
#define INCREMENT 3 /* Angle Increment */
#define MAXITEMS 5 /* 5 menu items */
#define MAXCOORDS 30 /* Max 30 pixels for outline */
#define MAXROTS 60 /* 60 roatations */
#define RastPort Window->RPort /* Simple access to the */
/* window's RastPort */
#define MAXAREA (MAXCOORDS-1)*MAXROTS
/* How many areas */
char ASCString[6]; /* for itoa() */
struct GfxBase *GfxBase = 0;
struct IntuitionBase *IntuitionBase = 0;
struct Screen *Screen = 0;
struct Window *Window = 0; /* Basepointers */
struct MenuItem Items[MAXITEMS]; /* Menu entries */
struct IntuiText Texts[MAXITEMS]; /* Menu Text */
struct Menu Menus; /* Menu */
struct NewWindow NewWindow;
struct NewScreen NewScreen;
char *IString[MAXITEMS] = {
0x000,0x0111,0x0222,0x0333,
0x0444,0x0555,0x0666,0x0777,
0x0888,0x0999,0x0aaa,0x0bbb,
0x0ccc,0x0ddd,0x0eee,0x0fff
};
/* Own color table with grey shades */
long Status = TRUE;
struct TmpRas TmpRas; /* TmpRas for Area()..... */
struct AreaInfo AreaInfo; /* AreaInfo for Area().... */
long *Pointer = 0; /* Help pointer */
UBYTE AreaBuffer[(4+1)*5]; /* Pixel storage for Area. */
/* Always 4 (+1) pixels per area */
PAGE 358
---------------------------------------------------------------------------
int sintab[91] = {
0, 285, 571, 857,1142,1427,1712,1996,2280,
2563,2845,3126,3406,3685,3963,4240,4516,4790,
5062,5334,5603,5871,6137,6401,6663,6924,7182,
7438,7691,7943,8192,8438,8682,8923,9161,9397,
9630,9860,10086,10310,10531,10748,10963,11173,11381,
11584,11785,11982,12175,12365,12550,12732,12910,13084,
13254,13420,13582,13740,13894,14043,14188,14329,14466,
14598,14725,14848,14967,15081,15190,15295,15395,15491,
15582,15662,15749,15825,15897,15964,16025,16082,16135,
16182,16224,16261,16294,16321,16244,16361,16374,16381,
16384
};
/* Sine table is calculated : */
/* sintab[x]=sin[x] * 16384 (x=1,2,...,90) */
/* See sincos(), sin(), cos() */
int Pixel[MAXROTS][MAXCOORDS][3];
int Mother[MAXROTS][MAXCOORDS][3];
/* Mother array is calculated for the Rotation */
/* object from the input outlines and is then */
/* Arrays. This is necessary because through the */
/* many transformations a single Array would suffer */
/* irreparable damage that would have a negative */
/* effect on the visible object */
int Area[MAXREAD][4][3];
/* Here we store the Areas calculated from the */
/* Pixels. */
int CoordArray[MAXCOORDS][2];
/* Here we store the coordinates for the entire */
/* outlines. */
int x[MAXROTS][MAXCOORDS];
int y[MAXROTS][MAXCOORDS];
/* For transformation 3D -> 2D (Screen) */
int Index[MAXAREA+1]; /* For sorting */
long ProzZ = 1500, /* Projections Central *
ProzY = 0,
ProzX = 0;
long d = 0; /* Separate projection plane */
/* Projection Central */
long bbpx = 0,
bbpy = 0, /* Observation Points */
bbpz = 0;
long AngleX = 0,
AngleY = 0, /* Display Angle */
AngleZ = 0;
PAGE 359
---------------------------------------------------------------------------
VOID Show();
long Average();
char *Itoa();
VOID Shade();
VOID Init();
VOID Rot();
long sincos();
long sin();
long cos();
long EnterCoords();
VOID make_menu();
struct IORequest *CreateExtIo();
VOID DeleteExtIo();
VOID HardCopy();
/********************************************************/
/* This function initialises the specified NewScreen */
/* and NewWindow structures for OpenScreen(), and */
/* OpenWindow(). */
/*------------------------------------------------------*/
/* Entry Parameters: to initialise NewScreen - */
/* structure (NS) */
/* to initialise NewWindow - */
/* structure (NW) */
/*------------------------------------------------------*/
/* Returned Values : None */
/********************************************************/
VOID InitScreenWindow (NS,NW)
struct NewScreen *NS;
struct NewWindow *NW;
{
NS->LeftEdge = 0; NS->TopEdge = 0;
NS->Width = WIDTH; NS->Height= HEIGHT;
NS->Depth = 4;
NS->DetailPen = 1; NS->BlockPen = 0;
NS->ViewModes = 0;
if (WIDTH > 320) NS->ViewModes |= HIRES;
if (HEIGHT > 200) NS->ViewModes := LACE;
NS->Type = CUSTOMSCREEN;
NS->Font = NULL;
NS->DefaultTitle = "";
NS->Gadgets = NULL; NS->CustomBitMap = NULL;
NW->LeftEdge = 0; NW->TopEdge = 0;
NW->Width = WIDTH; NW->Height = HEIGHT;
NW->DetailPen = 6; NW->BlockPen = 0;
NW->IDCMPFlags = NULL;
NW->Flags = BORDERLESS | ACTIVATE | NOCAREREFRESH
| REPORTMOUSE;
NW->FirstGadget = NULL;
NW->CheckMark = NULL; NW->Title = "";
NW->Screen = NULL; NW->BitMap = NULL;
NW->MinWidth = NW->MinHeight = NW->MaxWidth =
NW->MaxHeight = 0;
NW->Type =CUSTOMSCREEN;
}
PAGE 360
---------------------------------------------------------------------------
/*********************************************************/
/* This function builds the mnu and the individual */
/* Items. */
/*-------------------------------------------------------*/
/* Entry Parameters : None */
/*-------------------------------------------------------*/
/* Returned Values : None */
/*********************************************************/
VOID BuildUpMenu()
{
long i;
for (i=0; i<MAXITEMS; i++)
{
Items[i].LeftEdge = 1;
Items[i].TopEdge = i*10;
Items[i].Width = 112; Items[i].Height = 10;
Items[i].Flags = ITEMTEXT | ITEMENABLED | HIGHCOMP;
Items[i].MutualExclude = NULL;
Items[i].ItemFill = (APTR) &Texts[i];
Items[i].SelectFill = NULL;
Items[i].Command = NULL;
Items[i].SubItem = NULL;
Items[i].NextSelect = NULL;
Texts[i].FrontPen = 6; Texts[i].BackPen = 0;
Texts[i].DrawMode = JAM1; Texts[i].LeftEdge = 1;
Texts[i].TopEdge = 1;
Texts[i].ITextFont = NULL;
Texts[i].IText = IString[i];
Texts[i].NextText = NULL;
}
PAGE 361
---------------------------------------------------------------------------
for (i=0; i<MAXITEMS-1; i++)
Items[i].NextItem = &Items[i+1];
Items[MAXITEMS-1].NextItem = NULL;
Menus.NextMenu =NULL; Menus.LeftEdge = 10;
Menus.TopEdge = 0; Menus.Width = 110;
Menus.Height = 10; Menus.Flags = MENUENABLED;
Menus.MenuName = " Projects";
Menus.FirstItem = &Items(0);
}
/********************************************************/
/* This function ends the Program, but not before */
/* closing all the open libraries, Screens and Windows */
/* and giving back all the extra allocated memory. */
/*------------------------------------------------------*/
/* Entry Parameters : Output string (poss. Errormessage)*/
/*------------------------------------------------------*/
/* Returned Values : None */
/********************************************************/
VOID CloseIt(s)
char *s;
{
puts(s);
if (Pointer) FreeMem(Pointer,RASSIZE(WIDTH, HEIGHT/2));
/* TmpRas memory */
if (Window) {
ClearMenuStrip(Window);
CloseWindow(Window);
}
if (Screen) CloseScreen(Screen);
if (GfxBase) CloseLibrary(GfxBase);
if (IntuitionBase) CloseLibrary(IntuitionBase);
exit(0);
}
/********************************************************/
/* This opens all the required (Library, Screen, */
/* Window) for the Program. */
/*------------------------------------------------------*/
/* Entry Parameters : None */
/*------------------------------------------------------*/
/* Returned Values : None */
/********************************************************/
VOID OpenLibs()
{
InitScreenWindow(&NewScreen, &NewWindow);
if ((GfxBase = (struct GfxBase *)
OpenLibrary("graphics.library",0)) == 0)
CloseIt("No graphics !!!");
if ((IntuitionBase = (struct IntuitionBase *)
OpenLibrary("intuition.library",0)) == 0)
CloseIt(" No intuition !!!");
PAGE 362
---------------------------------------------------------------------------
if ((Screen = (struct Screen *)
OpenScreen(&NewScreen)) == 0)
CloseIt(" No Screen !!!");
NewWindow.Screen = Screen;
if ((Window = (struct Window *)
OpenWindow(&NewWindow)) == 0)
CloseIt( "No Window !!!");
LoadRB4(&Screen->ViewPort, &Colors[0], 16);
/* Load new color */
RemakeDisplay(); /* and display it */
SetRGB4(&Screen->ViewPort,17,4,4,4); /* Mouse pointer */
SetRGB4(&Screen->ViewPort,18,8,8,8); /* color */
SetRGB4(&Screen->ViewPort,19,13,13,13);
Init();
BuidlUpMenu();
}
/****************************************************************/
/* This function switches the Intuition message transfer off. */
/* This is important since normally all Intuition messages are */
/* stored in a list (Queue) and processed once after another. */
/* Because we cannot react in time to these messages due to */
/* calculations and drawing, it is best that you switch it off. */
/* Otherwise messages are stored (like a keypress) and then */
/* later processed which would be confusing to the user. */
/* For example, after a rotation, the key press is acted on */
/* that you pressed some time ago. */
/*--------------------------------------------------------------*/
/* Entry Parameters : None. */
/*--------------------------------------------------------------*/
/* Returned Values : None. */
/****************************************************************/
VOID IDCMPoff()
{
SetMenuStrip(Window,NULL);
ModifyIDCMP(Window,NULL);
/* Separate Menu Row From Window. */
}
/****************************************************************/
/* This allows intuition to send messages again. */
/*--------------------------------------------------------------*/
/* Entry Parameters : None. */
/*--------------------------------------------------------------*/
/* Returned Values : None. */
/****************************************************************/
PAGE 363
---------------------------------------------------------------------------
VOID IDCMPon()
{
ModifyIDCMP(Window, RAWKEY | MOUSEBUTTONS | MENUPICK);
SetMenuStrip(Window,&Menus); /* Link menu with */
/* Window. */
}
/****************************************************************/
/* This routine uses the shape outline to calculate the */
/* coordinates for the 3 dimensional rotation figure. Rotation */
/* 3D (Y axis). */
/*--------------------------------------------------------------*/
/* Entry Parameters : */
/* rot = Number of Rotations. */
/* ko = Number of coordinates for outlines */
/*--------------------------------------------------------------*/
/* Returned Values : none. */
/****************************************************************/
VOID Rotate(rot,ko)
long rot,ko;
{
long i,j;
long s,c;
Move(RastPort,0,RastPort->TxBaseline);
Text(RastPort,"Calculating Rotation Matrix ...",29);
for(i=0; i<rot; i++)
{
s=sin((360/rot)*i); /* Calculate rotation angle */
c=cos((360/rot)*i);
for (j=0; j<ko; j++)
{
Pixel[i][j][0] = Mother[i][j][0] =
(CoordArray[j][0]*c)/16384; /* X */
Pixel[i][j][1] = Mother[i][j][1] =
CoordArray[j][1]; /* Y */
Pixel[i][j][2] = Mother[i][j][2] =
-(CoordArray[j][0]*s)/16384; /* Z */
/* 'Mother' is inchanged and is used by the entire program */
/* as a mother-Mask. */
}
}
Move (RastPort,0,RastPort->TxBaseline);
Text (RastPort," ",29);
/****************************************************************/
/* This routine calculates and draws the 2 dimensional picture */
/* from the three dimensional coordinates. */
/*--------------------------------------------------------------*/
/* Entry Parameters : rot = Number of Rotations. */
/* ko = Number of Coordinates for the */
/* outline. */
/*--------------------------------------------------------------*/
/* Returned Values : None. */
/****************************************************************/
PAGE 364
---------------------------------------------------------------------------
VOID Show(rot,ko)
long rot,ko;
{
long i,j,t;
SetRast(RastPort,0);
for(i=0; i<rot; i++)
for(j=0; j<ko; j++)
{
t = (d-pixel[i][j][2])/(ProzZ-Pixel[i][j][2]);
x[i][j] = WIDTH/2-Pixel[i][j][0]+
(ProzX-Pixel[i][j][0])*t;
y[i][j] = HEIGHT/2-Pixel[i][j][1]+(ProzY-
Pixel[i][j][1])*t;
/* 3d - 2d transformation */
if(j>0)
{
Move(RastPort,x[i][j-1],y[i][j-1]);
Draw(RastPort,x[i][j],y[i][j]);
/* Longitudinal Support */
}
if(i>0)
{
Move(RastPort,x[i-1][j],y[i-1][j]);
Draw(RastPort,x[i][j], y[i][j]);
/* Transverse support */
}
}
for (j=0; j<ko; j++)
{
Move(RastPort,x[0][j]. y[0][j]);
Draw(RastPort,x[rot-1][j],y[rot-1][j]);
/* Last transverse */
}
/****************************************************************/
/* This routine calculates the middle 2 coordinates for a */
/* specified area. */
/*--------------------------------------------------------------*/
/* Entry Parameters : i = Which area. */
/*--------------------------------------------------------------*/
/* Returned Value : Middle Z coordinate for area 1 */
/****************************************************************/
long Average(i)
long i;
{
PAGE 365
---------------------------------------------------------------------------
long a,b,c,d;
a=Area[i][0][2];
b=Area[i][1][2];
c=Area[i][2][2];
d=Area[i][3][2];
a=a+b+c+d; /* Picture Sum and Average */
return(a/4);
}
/****************************************************************/
/* This routine converts the received Integer number to */
/* ASCII. */
/*--------------------------------------------------------------*/
/* Entry Parameters : x = Integer. */
/* position = How many positions for */
/* entire Ascii string. */
/*--------------------------------------------------------------*/
/* Returned Value : Address of ASCII string. */
/****************************************************************/
char *Itoa(x,position)
long x,position;
{
position--;
if (x<0)
{
ASCString[0] = '-';
x= -x;
}
else ASCString[0] = 32;
do {
ASCString[position] = (x % 10)+'0'; /* + '0' */
position--;
x/=10;
} while (position > 0);
return (ASCString);
}
/****************************************************************/
/* This routine calculates and draws the Areas of the Rotation */
/* Object. */
/*--------------------------------------------------------------*/
/* Entry Parameters : rot - Number of Rotations. */
/* ko - Number of pixels per rotation line. */
/* mode- Color Shading ? */
/* Status - Recalculate Area ? */
/*--------------------------------------------------------------*/
/* Returned Values : None. */
/****************************************************************/
VOID Shade(rot, ko, Mode, Status)
long rot, ko, Mode, Status;
PAGE 366
---------------------------------------------------------------------------
long i,,k;
long count; /* Number of areas for Rot.Object. */
long hilf;
long x,y;
long t;
long a,d,c,e;
long w,col;
count = 0;
IDCMPoff(); /* No messages from intuition */
if (Status == FALSE) /* recalculate area */
{
Move(RastPort,0,RastPort->TxBaseline);
Text(RastPort,"Calculating Areas... ",20);
for(i=0; i<(rot-1); i++)
for(j=0; j<(ko-1); j++)
{
for (k=0; k<3; k++)
{
Area[count][0][k] = Pixel[i][j][k];
Area[count][1][k] = Pixel[i][j+1][k];
Area[count][2][k] = Pixel[i+1][j+1][k];
Area[count][3][k] = Pixel[i+1][j][k];
}
count++;
}
for(j=0; j<(ko-1); j++)
{
for (k=0; k<3; k++)
{
Area[count][0][k] = Pixel[rot-1][j][k];
Area[count][1][k] = Pixel[rot-1][j+1][k];
Area[count][2][k] = Pixel[0][j+1][k];
Area[count][3][k] = Pixel[0][j][k];
}
count++;
}
Move (RastPort,0,RastPort->TxBaseline);
Text (RastPort,"Sorting Areas ... ",20);
x = RastPort->cp_x;
y = RastPort->cp_y;
for(i=0; i<count; i++) Index[i+1] = 1;
/* Initialise Index Array */
for(i=2; i<count+1; i++)
{
/* Zero area components for sorting ! */
hilf = Average(Index[i]);
PAGE 367
----------------------------------------------------------------------------
Index[0] = Index[i];
j= i-1;
while(hilf < Average(Index[j]))
{
Index[j+1] = Index[j];
j--;
}
Index[j+1] = Index[0];
Move (RastPort,x,y);
Text (RastPort,itoa((count-1),5),5);
}
}
count = (ko-1)*rot+1; /* Count must always */
SetRast(RastPort,0); /* Be checked. */
col =0;
if (Mode == 0)
{
SetAPen(RastPort,0); /* Clear Areas and only */
SetAPen(RastPort,9); /* draw frame. */
}
else
{
w=count/9; /* Grey scale calculations */
SetOPen(RastPort,0); /* No frame. */
}
for(i=1; i<count; i++)
{
e=Index[i];
for(j=0; j<4; j++) /* Draw areas. */
{
a = Area[e][j][0];
b = Area[e][j][1];
c = Area[e][j][2];
t = (d-c)/(ProzZ-c);
x = WIDTH/2-a+(ProzZ-a)*t;
y = HEIGHT/2-b+(ProzY-b)*t;
if (Mode == 1) /* In Grey ? */
{
if (i==w)
{
w += count/9;
col++;
}
SetAPen(RastPort,4+col);
}
if (j==0) AreaMove (RastPort,x,y);
PAGE 368
---------------------------------------------------------------------------
else AreaDraw(RastPort,x,y);
/* New polygon pixel */
}
AreaEnd(RastPort);
/* Polygon end + draw */
}
SetAPen(RastPort,9);
}
/****************************************************************/
/* This routine initialises the TmpRas and AreaInfo structure */
/* for the RastPort. */
/*--------------------------------------------------------------*/
/* Entry Parameters : */
/*--------------------------------------------------------------*/
/* Returned Values : None */
/****************************************************************/
VOID Init()
{
InitArea(&AreaInfo,AreaBuffer,5);
/* Always 4 (+1) coordinates */
if ((Pointer = (long *)AllocRaster(WIDTH, HEIGHT/2)) == 0)
CloseIt("No free space !!");
/* memory for TmpRas */
RastPort->AreaInfo = &AreaInfo;
RastPort->TmpRas = (struct TmpRas *)
InitTmpRas(&TmpRas,Pointer,RASSIZE(WIDTH,HEIGHT/2);
}
/****************************************************************/
/* This routine calculates the new position for the rotation */
/* object. */
/*--------------------------------------------------------------*/
/* Entry Parameters : AngleX, AngleY, AngleZ determine */
/* the new object position. */
/* rot - Number of rotations. */
/* ko - Number of line per rotation line. */
/*--------------------------------------------------------------*/
/* Returned Values : None. */
/****************************************************************/
VOID Rot(AngleX, AngleY, AngleZ, rot, ko)
long AngleX, AngleY, AngleZ, rot, ko;
{
long sx, cx, sy, cy, sz, cz;
long i,j;
long hx, hy, hz;
Move (RastPort,0,RastPort->TxBaseline);
Text (RastPort,"Calculating new position....",25);
PAGE 369
----------------------------------------------------------------------------
sx=sin(AnlgeX); /* Calculate sin(x,y,z) and */
cx=cos(AngleX); /* cos(x,y,z) only once */
sy=sin(AngleY);
cy=cos(AnlgeY);
sz=sin(AngleZ);
cz=cos(AnlgeZ);
for (i=0; i<rot; i++) /* Calculate global rotation */
for(j=0; j<ko; j++)
{
hy = Mother[i][j][1] * cx/16384-Mother[i][j][2]*sx/16384;
hz = Mother[i][j][1] * sx/16384+Mother[i][j][2]*cx/16384;
Pixel[i][j][0] = Mother[i][j][0];
Pixel[i][j][1] = hy;
Pixel[i][j][2] = hz;
hx = Pixel[i][j][0]*cy/16384+Pixel[i][j][2]*sy/16384;
hz =-Pixel[i][j][0]*sy/16384+Pixel[i][j][2]*cy/16384;
Pixel[i][j][0] = hx;
Pixel[i][j][2] = hz+bbpz;
hx=Pixel[i][j][0] *cz/16384-Pixel[i][j][1]*sz/16384;
hy=Pixel[i][j][0] *sz/16384+Pixel[i][j][1]*cz/16384;
Pixel[i][j][0] = hx+bbpx;
Pixel[i][j][1] = hy+bbpy;
}
Move(RastPort,0,RastPort->TxBaseline);
Text(RastPort," ",25);
}
/********************************************************/
/* This function gets the Sine Value from the table. */
/*------------------------------------------------------*/
/* Entry Parameters : x for sin(x). */
/*------------------------------------------------------*/
/* Returned Value : sin(x) * 16384 */
/********************************************************/
long sincos(x)
long x;
{
long factor = 1;
x %= 360;
if (x>180) /* x > 180 degrees */
{
x -=180;
factor = -1;
}
PAGE 370
---------------------------------------------------------------------------
if (x>90) x = 180 - x;
return(sintab[x]*factor);
}
/********************************************************/
/* This function calculates the Sine for x */
/*------------------------------------------------------*/
/* Entry PArameter : x */
/*------------------------------------------------------*/
/* Returned Value : sin(x) * 16384 */
/********************************************************/
long sin(x)
long x;
{
return(sincos(x));
}
/*********************************************************/
/* This function calculates the Cosine for x. */
/*-------------------------------------------------------*/
/* Entry PArameter : x */
/*-------------------------------------------------------*/
/* Returned Value : cos(x) * 16384 */
/*********************************************************/
long cos(x)
long x;
{
return (sincos(x+90)); /* cos(x) = sin (90+x) */
}
/********************************************************/
/* This function allows the input for the outlines */
/*------------------------------------------------------*/
/* Entry Parameters : rot - How many rotations are */
/* allowed. */
/*------------------------------------------------------*/
/* Returned Values : Number of entered Coordinates. */
/********************************************************/
long EnterCoords(rot)
long rot;
{
long Class, /* For specifying the */
Code; /* Intuition Messages.*/
long Position; /* Position where the coordinates */
long x,y; /* are stored in memory. */
long oldkoorx,oldkoory,oldx,oldy;
long Ende = FALSE; /* Entry Complete ? */
ModifyIDCMP(Window, MOUSEMOVE | MOUSEBUTTONS | RAWKEY );
PAGE 371
---------------------------------------------------------------------------
/* Entry only requires */
/* Mouse and Keyboard */
SetRast(RastPort,0); /* Erase bitmap */
SetAPen(RastPort,9); /* APen, BPen and DrawMode */
SetBPen(RastPort,0); /* setup. */
SetDrMd(RastPort,JAM2);
Position = 0; /* Where are Coordinates stored ? */
Move(RastPort,WIDTH/2,0); /* Axis intersections */
Draw(RastPort,WIDTH/2,HEIGHT);
Move(RastPort,0,HEIGHT/2);
Draw(RastPort,WIDTH,HEIGHT/2);
x=Screen->MouseX; /* Actual mouse position for o/p */
y=Screen->MouseY;
while (!Ende && (Position < MAXCOORDS))
{
if (1 << Window->UserPort->mp_SigBit)
/* Any messages from Intuition ? */
while (Message = (struct IntuiMessage *)
GetMsg(Window->UserPort)) /* YES */
{
Class = Message->Class; /* Get required */
Code = Message->Code; /* Data */
x = Message->MouseX;
y = Message->MouseY;
ReplyMsg(Message); /* Have found */
/* Message ! */
if(Position == 0)
{
oldx = x;
oldy = y;
}
switch(Class)
{
case MOUSEBUTTONS:
if (Code == SELECTDOWN)
{
/* Coordinates Relative */
/* to axis intersection! */
if(Position == 0)
{
oldkoorx = x;
oldkoory = y;
}
PAGE 372
---------------------------------------------------------------------------
CoordArray[Position][0]= x-WIDTH/2;
CoordArray[Position][1]= HEIGHT/2-y;
SetDrMd(RastPort,JAM2);
Move(RastPort,oldkoorx,oldkoory);
Draw(RastPort,x,y);
/* Draw small intersection */
Move(RastPort,x-3,y-3);
Draw(RastPort,x+3,y+3);
Move(RastPort,x-3,y+3);
Draw(RastPort,x+3,y-3);
Move(RastPort,x,y);
/* Old cursor position */
oldkoorx = x;
oldkoory = y;
Position++;
}
case MOUSEMOVE:
if (Position > 0)
{
if ((x != oldx) || (y!=oldy))
{
SetDrMd(RastPort, COMPLEMENT | JAM1);
Move(RastPort,oldkoorx, oldkoory);
Draw(RastPort,oldx,oldy);
Move(RastPort,oldkoorx,oldkoory);
Draw(RastPort,x,y);
/* Draw line to Actual */
/* Mouse position */
oldx = x;
oldy = y;
}
}
break;
case RAWKEY:
if ((Code = 0x50) && (Position > 0))
Ende = TRUE;
PAGE 373
---------------------------------------------------------------------------
/* F1 Pressed */
break;
}
}
SetDrMd(RastPort,JAM2);
/* Output Mouse position */
Move(RastPort,3*WIDTH/4,RastPort->TxBaseline;
Text(RastPort,"X: ",3);
Text(RastPort,itoa((x-WIDTH/2),4),4);
Text(RastPort," Y: ",4);
Text(RastPort,itoa(HEIGHT/2-y),4),4);
}
SetDrMd(RastPort, COMPLEMENT | JAM1);
Move(RastPort,oldkoorx,oldkoory);
Draw(RastPort,oldx,oldy);
SetDrMd(RastPort,JAM2);
Rotate(rot,Position); /* Calculate rotation objects */
return(Position);
}
/********************************************************/
/* This routine allows the User to control the Rotation */
/* Object. */
/*------------------------------------------------------*/
/* Entry Parameters : rot = number of rotation lines. */
/* ko = number of pixels per */
/* rotation line. */
/*------------------------------------------------------*/
/* Returned Value : none */
/********************************************************/
VOID make_menu(rot,ko)
long rot,ko;
{
long Ende = FALSE; /* Program end ? */
long First= TRUE; /* First time shading ? */
long Class,Code; /* Intuition's messages */
long x,y;
IDCMPon(); /* Message Please ! */
while (!Ende)
{
Wait(1 << Window->UserPort->mp_SigBit);
/* Answered ? */
while (Message = (struct IntuiMessage *)
GetMsg(Window->UserPort)) /* YES */
{
PAGE 374
----------------------------------------------------------------------------
Class= Message->Class;
Code = Message->Code;
ReplyMsg(Message);
/* We have received it, Thanks */
switch (class)
{
case RAWKEY:
x=y=0;
switch (code)
{
case 0x4c: /* Arrow Keys */
y = -1;
break;
case 0x4d:
y = 1;
break;
case 0x4f:
x=-1;
break;
case 0x4e:
x=1;
break;
}
if ((x==0) && (y==0)) Status = TRUE;
else
{
IDCMPoff(); /* No intuition */
if ((y == -1) && (AngleX == 0))
AngleX=360;
if ((x == -1) && (AngleZ == 0))
AngleZ=360;
AngleX += y*INCREMENT;
AngleZ += x*INCREMENT;
Rot(AngleX, AngleY, AngleZ, rot, ko);
Show(rot,ko);
IDCMPon();
First = TRUE;
Status = FALSE;
}
break;
case MENUPICK:
if (Code!=MENUNULL)
switch(MENUNUM(Code))
{
case 0:
switch(ITEMNUM (Code))
{
/* Shading */ case 0:
IDCMPoff();
if ((First) || (!Status))
Shade(rot,ko,1,FALSE);
else
Shade(rot,ko,1,TRUE);
PAGE 375
---------------------------------------------------------------------------
First = FALSE;
Status = TRUE;
IDCMPon();
break;
/* Hide it */ case 1:
IDCMPoff();
if((First) || (Status))
Shade(rot,ko,0,FALSE);
else
Shade(rot,ko,0,TRUE);
First = FALSE;
Status = TRUE;
IDCMPon();
break;
/* New coordinates */ case 2:
IDCMPoff();
ko = EnterCoords(MAXROTS);
AngleX = 0;
AngleZ= 0;
/* AngleY = 0 */
Show(rot,ko);
First = TRUE;
Status = FALSE;
IDCMPon();
break;
/* HardCopy */ case 3:
IDCMPoff();
hardcopy();
IDCMPon();
break;
/* Quit */ case 4:
Ende = TRUE;
break;
}
}
}
}
}
}
PAGE 376
---------------------------------------------------------------------------
/********************************************************/
/* Here is where the HardCopy routines start! Both of */
/* the following functions are used for 'DumpRastPort' */
/* (External Input / Output ) */
/*-------------------------------------------------------*/
struct IORequest *CreateExtIO(ioReplyPort,size)
struct MsgPort, *ioReplyPort;
long size;
struct IORequest *ioreq;
if (ioReplyPort == NULL) return (NULL);
oiReq = (struct IORequest *) AllocMem(size,MEMF_CLEAR);
if(ioReq == NULL) return (NULL);
ioReq->io_Message.mn_Node.ln_Type = NT_MESSAGE;
ioReq->io_Message.mn_Length = size;
ioReq->io_Message.mn_ReplyPort = ioReplyPort;
return(ioReq);
}
VOID DeleteExtIO(ioExt)
struct IORequest *ioExt;
{
if(ioExt)
{
ioExt->io_Message.mn_Node.ln_Type = 0xff;
ioExt->io_Device = (struct Device *)-1;
ioExt->io_Unit = (struct Unit *)-1;
FreeMem(ioExt, ioExt->io_Message.mn_Length);
}
}
/************************************************/
/ Here are the actual hardcopy routines. */
/************************************************/
VOID hardcopy()
union printerIO
{
struct IOStdReq ios;
struct IODRPReq iodrp;
struct IOPrtCmdReq iope;
};
union printerIO *request;
struct MsgPort *printerPort;
printerPort = CreatePort("printer.port",0);
request = (union printerIO *)CreateExtIO(printerport,
sizeof(union,printerIO));
if (OpenDevice("printer.device",0,request,0) != 0)
{
DeleteExtIO(request); /* Open Printer. */
DeletePort(printerPort);
CloseIt("Sorry, no Printer !! ");
}
PAGE 377
--------------------------------------------------------------------------
request->iodrp.io_Command = PRD_DUMPRPORT;
/* Dump RastPort command */
request->iodrp.io_RastPort = RastPort;
/* Which RastPort ? */
SetRGB4(&Screen-ViewPort,0,15,15,15);
/* Background color = white */
request->iodrp.io_ColorMap = Screen->ViewPort.ColorMap;
/* For shading on the printer */
request->iodrp.io_Modes = NewScreen.ViewModes;
/* Which Viewmode ? */
request->iodrp.io_ScrX = 0; /* Upper left corner */
request->iodrp.io_ScrY = 0;
request->iodrp.io_ScrWidth = WIDTH;
request->iodrp.io_ScrHeight = HEIGHT;
/* Lower right */
/* Corner */
request->iodrp.io_DestCols = 0;
request->iodrp.io_DestRows = 0;
request->iodrp.io_Special = 0x004; /* Flag */
DoIO(request); /* printing */
CloseDevice(request);
DeleteExtIO(request); /* Close everything */
DeletePort(printerPort);
SetRGB4(&Screen->ViewPort,0,0,0,0);
/* Background back to normal */
}
/****************************************************************/
/* here is the MAIN PROGRAM : */
/****************************************************************/
main()
{
long Coords;
OpenLibs; /* Open libraries */
Coords = EnterCoords(MAXROTS); /* GET OUTLINE */
show(MAXROTS,Coords); /* Draw Object */
make_menu(MAXROTS,Coords); /* Menu */
CloseIt("Bye Bye");
return(0);
}
PAGE 378
---------------------------------------------------------------------------
THE PROGRAM:
------------
After the program starts you can use the mouse to enter the outline of your
object. When you finish setting all the pixels that design your object,
press the <F1> key to see the three dimensional object on the screen. The
object will automatically be generated without using the <F1> key if you
have reached the value of MAXCOORDS that is alowed.
You can now use the cursor keys to rotate your object.
You can do much more with the object you just created. To display the
object with a shaded grey scale, select the menu item Shading.
We create the shading effect with a simple algorithm. First we calculate an
area list that contains the coordinates of all the filled area's corner
points. We then sort these points by their middle Z coordinate. After this,
we first draw the areas that are furthest away from the viewer (naturally
using Area... functions).
We use an incrementing counter that changes the shading colour after
drawing a specified number of areas.
The way the menu item 'Hide it' functions is similar to the shading
functions. Instead of shading the areas, it makes the area colours equal
to the background color and therefore hides them. The framing color in OPen
displays the object and 'Hide it' simulates a hidden line algorithm.
When you get tired of your current rotation object you can select
Coordinates from the menu and make a new object.
The menu items Hardcopy and Quit are self-explanatory.
Please make sure that the routines for calculating the rotation object are
kept together. Also, you can create you own objects (not just rotation
objects) by providing the three dimensional coordinates yourself. With
a slightly modified show routine and help from Rot, you can create and
display your own rotation objects.
PAGE 379
---------------------------------------------------------------------------
> Now load part 5 <
---------------------------------------------------------------------------